From d730c1263328c5990ce46cdf6394ce6e36cc3609 Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Sun, 22 Feb 2009 23:19:25 +0000 Subject: [PATCH] Add axTLS sourcecode --- NOTICE | 1 + libs/nixio/.gitignore | 2 +- libs/nixio/Makefile | 8 +- libs/nixio/axTLS/.prepared | 0 libs/nixio/axTLS/Makefile | 181 + libs/nixio/axTLS/README | 3 + libs/nixio/axTLS/_stage/libaxtls.a | Bin 0 -> 110900 bytes libs/nixio/axTLS/_stage/libaxtls.so.1 | 1 + libs/nixio/axTLS/_stage/libaxtls.so.1.2 | Bin 0 -> 82329 bytes libs/nixio/axTLS/bindings/Config.in | 105 + libs/nixio/axTLS/bindings/Makefile | 86 + libs/nixio/axTLS/bindings/README | 43 + libs/nixio/axTLS/bindings/csharp/Makefile | 35 + libs/nixio/axTLS/bindings/csharp/axTLS.cs | 491 ++ .../axTLS/bindings/generate_SWIG_interface.pl | 393 ++ libs/nixio/axTLS/bindings/generate_interface.pl | 322 + libs/nixio/axTLS/bindings/java/Makefile | 94 + libs/nixio/axTLS/bindings/java/SSL.java | 137 + libs/nixio/axTLS/bindings/java/SSLCTX.java | 229 + libs/nixio/axTLS/bindings/java/SSLClient.java | 81 + libs/nixio/axTLS/bindings/java/SSLReadHolder.java | 61 + libs/nixio/axTLS/bindings/java/SSLServer.java | 72 + libs/nixio/axTLS/bindings/java/SSLUtil.java | 116 + libs/nixio/axTLS/bindings/lua/Makefile | 67 + libs/nixio/axTLS/bindings/perl/Makefile | 91 + libs/nixio/axTLS/bindings/vbnet/Makefile | 35 + libs/nixio/axTLS/bindings/vbnet/axTLSvb.vb | 200 + libs/nixio/axTLS/config/.config | 112 + libs/nixio/axTLS/config/Config.in | 114 + libs/nixio/axTLS/config/JMeter.jmx | 247 + libs/nixio/axTLS/config/Rules.mak | 220 + libs/nixio/axTLS/config/axhttpd.aip | 136 + libs/nixio/axTLS/config/axtls.RES | Bin 0 -> 22748 bytes libs/nixio/axTLS/config/axtls.rc | 32 + libs/nixio/axTLS/config/config.h | 113 + libs/nixio/axTLS/config/linuxconfig | 119 + libs/nixio/axTLS/config/makefile.conf | 127 + libs/nixio/axTLS/config/makefile.dotnet.conf | 65 + libs/nixio/axTLS/config/makefile.java.conf | 58 + libs/nixio/axTLS/config/makefile.post | 19 + .../config/scripts/config/Kconfig-language.txt | 255 + libs/nixio/axTLS/config/scripts/config/Makefile | 121 + libs/nixio/axTLS/config/scripts/config/conf.c | 583 ++ libs/nixio/axTLS/config/scripts/config/confdata.c | 458 ++ libs/nixio/axTLS/config/scripts/config/expr.c | 1099 +++ libs/nixio/axTLS/config/scripts/config/expr.h | 195 + .../config/scripts/config/lex.zconf.c_shipped | 3688 ++++++++++ libs/nixio/axTLS/config/scripts/config/lkc.h | 123 + libs/nixio/axTLS/config/scripts/config/lkc_proto.h | 40 + .../config/scripts/config/lxdialog/BIG.FAT.WARNING | 4 + .../config/scripts/config/lxdialog/checklist.c | 372 + .../axTLS/config/scripts/config/lxdialog/colors.h | 161 + .../axTLS/config/scripts/config/lxdialog/dialog.h | 199 + .../config/scripts/config/lxdialog/inputbox.c | 240 + .../axTLS/config/scripts/config/lxdialog/menubox.c | 438 ++ .../axTLS/config/scripts/config/lxdialog/msgbox.c | 85 + .../axTLS/config/scripts/config/lxdialog/textbox.c | 556 ++ .../axTLS/config/scripts/config/lxdialog/util.c | 375 ++ .../axTLS/config/scripts/config/lxdialog/yesno.c | 118 + libs/nixio/axTLS/config/scripts/config/mconf.c | 977 +++ libs/nixio/axTLS/config/scripts/config/menu.c | 390 ++ libs/nixio/axTLS/config/scripts/config/mkconfigs | 67 + libs/nixio/axTLS/config/scripts/config/symbol.c | 809 +++ libs/nixio/axTLS/config/scripts/config/util.c | 109 + libs/nixio/axTLS/config/scripts/config/zconf.l | 366 + .../config/scripts/config/zconf.tab.c_shipped | 2130 ++++++ .../config/scripts/config/zconf.tab.h_shipped | 125 + libs/nixio/axTLS/config/scripts/config/zconf.y | 690 ++ libs/nixio/axTLS/config/win32config | 119 + libs/nixio/axTLS/crypto/.depend | 19 + libs/nixio/axTLS/crypto/Makefile | 50 + libs/nixio/axTLS/crypto/aes.c | 456 ++ libs/nixio/axTLS/crypto/bigint.c | 1575 +++++ libs/nixio/axTLS/crypto/bigint.h | 99 + libs/nixio/axTLS/crypto/bigint_impl.h | 112 + libs/nixio/axTLS/crypto/crypto.h | 222 + libs/nixio/axTLS/crypto/crypto_misc.c | 357 + libs/nixio/axTLS/crypto/hmac.c | 100 + libs/nixio/axTLS/crypto/md2.c | 162 + libs/nixio/axTLS/crypto/md5.c | 293 + libs/nixio/axTLS/crypto/rc4.c | 91 + libs/nixio/axTLS/crypto/rsa.c | 268 + libs/nixio/axTLS/crypto/sha1.c | 248 + libs/nixio/axTLS/docsrc/Makefile | 39 + libs/nixio/axTLS/docsrc/axTLS.dox | 1237 ++++ libs/nixio/axTLS/docsrc/doco_footer.html | 3 + libs/nixio/axTLS/docsrc/images/axolotl.jpg | Bin 0 -> 3041 bytes libs/nixio/axTLS/docsrc/images/tsbasbw.gif | Bin 0 -> 2481 bytes libs/nixio/axTLS/httpd/Config.in | 163 + libs/nixio/axTLS/httpd/Makefile | 127 + libs/nixio/axTLS/httpd/axhttp.h | 154 + libs/nixio/axTLS/httpd/axhttpd.c | 604 ++ libs/nixio/axTLS/httpd/htpasswd.c | 136 + .../httpd/kepler-1.1-snapshot-20070521-1825.tar.gz | Bin 0 -> 768249 bytes libs/nixio/axTLS/httpd/kepler.patch | 64 + libs/nixio/axTLS/httpd/proc.c | 1238 ++++ libs/nixio/axTLS/httpd/tdate_parse.c | 119 + libs/nixio/axTLS/samples/Config.in | 63 + libs/nixio/axTLS/samples/Makefile | 62 + libs/nixio/axTLS/samples/c/Makefile | 76 + libs/nixio/axTLS/samples/c/axssl.c | 883 +++ libs/nixio/axTLS/samples/csharp/Makefile | 48 + libs/nixio/axTLS/samples/csharp/axssl.cs | 758 +++ libs/nixio/axTLS/samples/java/Makefile | 51 + libs/nixio/axTLS/samples/java/axssl.java | 760 +++ libs/nixio/axTLS/samples/java/manifest.mf | 1 + libs/nixio/axTLS/samples/lua/Makefile | 43 + libs/nixio/axTLS/samples/lua/axssl.lua | 562 ++ libs/nixio/axTLS/samples/perl/Makefile | 43 + libs/nixio/axTLS/samples/perl/axssl.pl | 634 ++ libs/nixio/axTLS/samples/vbnet/Makefile | 48 + libs/nixio/axTLS/samples/vbnet/axssl.vb | 702 ++ libs/nixio/axTLS/ssl/.depend | 31 + libs/nixio/axTLS/ssl/BigIntConfig.in | 132 + libs/nixio/axTLS/ssl/Config.in | 336 + libs/nixio/axTLS/ssl/Makefile | 123 + libs/nixio/axTLS/ssl/asn1.c | 510 ++ libs/nixio/axTLS/ssl/cert.h | 43 + libs/nixio/axTLS/ssl/crypto_misc.h | 164 + libs/nixio/axTLS/ssl/gen_cert.c | 363 + libs/nixio/axTLS/ssl/loader.c | 465 ++ libs/nixio/axTLS/ssl/openssl.c | 322 + libs/nixio/axTLS/ssl/os_port.c | 158 + libs/nixio/axTLS/ssl/os_port.h | 207 + libs/nixio/axTLS/ssl/p12.c | 486 ++ libs/nixio/axTLS/ssl/private_key.h | 54 + libs/nixio/axTLS/ssl/ssl.h | 474 ++ libs/nixio/axTLS/ssl/test/Makefile | 97 + libs/nixio/axTLS/ssl/test/axTLS.ca_key.pem | 15 + libs/nixio/axTLS/ssl/test/axTLS.ca_x509.cer | Bin 0 -> 483 bytes libs/nixio/axTLS/ssl/test/axTLS.ca_x509.pem | 13 + libs/nixio/axTLS/ssl/test/axTLS.device_key | Bin 0 -> 609 bytes libs/nixio/axTLS/ssl/test/axTLS.device_key.pem | 15 + libs/nixio/axTLS/ssl/test/axTLS.encrypted.p8 | Bin 0 -> 385 bytes libs/nixio/axTLS/ssl/test/axTLS.encrypted_pem.p8 | 11 + libs/nixio/axTLS/ssl/test/axTLS.key_1024 | Bin 0 -> 609 bytes libs/nixio/axTLS/ssl/test/axTLS.key_1024.pem | 15 + libs/nixio/axTLS/ssl/test/axTLS.key_2048 | Bin 0 -> 1191 bytes libs/nixio/axTLS/ssl/test/axTLS.key_2048.pem | 27 + libs/nixio/axTLS/ssl/test/axTLS.key_4096 | Bin 0 -> 2349 bytes libs/nixio/axTLS/ssl/test/axTLS.key_4096.pem | 51 + libs/nixio/axTLS/ssl/test/axTLS.key_512 | Bin 0 -> 321 bytes libs/nixio/axTLS/ssl/test/axTLS.key_512.pem | 9 + libs/nixio/axTLS/ssl/test/axTLS.key_aes128.pem | 12 + libs/nixio/axTLS/ssl/test/axTLS.key_aes256.pem | 12 + libs/nixio/axTLS/ssl/test/axTLS.noname.p12 | Bin 0 -> 1483 bytes libs/nixio/axTLS/ssl/test/axTLS.unencrypted.p8 | Bin 0 -> 347 bytes libs/nixio/axTLS/ssl/test/axTLS.unencrypted_pem.p8 | 10 + libs/nixio/axTLS/ssl/test/axTLS.withCA.p12 | Bin 0 -> 2089 bytes libs/nixio/axTLS/ssl/test/axTLS.withoutCA.p12 | Bin 0 -> 1573 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_1024.cer | Bin 0 -> 475 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_1024.pem | 12 + libs/nixio/axTLS/ssl/test/axTLS.x509_2048.cer | Bin 0 -> 607 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_2048.pem | 15 + libs/nixio/axTLS/ssl/test/axTLS.x509_4096.cer | Bin 0 -> 863 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_4096.pem | 20 + libs/nixio/axTLS/ssl/test/axTLS.x509_512.cer | Bin 0 -> 406 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_512.pem | 11 + libs/nixio/axTLS/ssl/test/axTLS.x509_aes128.pem | 11 + libs/nixio/axTLS/ssl/test/axTLS.x509_aes256.pem | 11 + libs/nixio/axTLS/ssl/test/axTLS.x509_bad_after.pem | 11 + .../nixio/axTLS/ssl/test/axTLS.x509_bad_before.pem | 11 + libs/nixio/axTLS/ssl/test/axTLS.x509_device.cer | Bin 0 -> 401 bytes libs/nixio/axTLS/ssl/test/axTLS.x509_device.pem | 24 + libs/nixio/axTLS/ssl/test/datatest.c | 43 + libs/nixio/axTLS/ssl/test/datatest.c.old | 280 + libs/nixio/axTLS/ssl/test/deutsche_telecom.x509_ca | Bin 0 -> 670 bytes libs/nixio/axTLS/ssl/test/equifax.x509_ca | Bin 0 -> 646 bytes libs/nixio/axTLS/ssl/test/gnutls.cer | Bin 0 -> 599 bytes libs/nixio/axTLS/ssl/test/header_issue.dat | Bin 0 -> 1159 bytes libs/nixio/axTLS/ssl/test/killopenssl.sh | 2 + libs/nixio/axTLS/ssl/test/make_certs.sh | 174 + libs/nixio/axTLS/ssl/test/microsoft.x509_ca | Bin 0 -> 1046 bytes libs/nixio/axTLS/ssl/test/microsoft.x509_ca.pem | 24 + libs/nixio/axTLS/ssl/test/ms_iis.cer | 13 + libs/nixio/axTLS/ssl/test/perf_bigint.c | 228 + libs/nixio/axTLS/ssl/test/socgen.cer | Bin 0 -> 980 bytes libs/nixio/axTLS/ssl/test/ssltest.c | 1983 ++++++ libs/nixio/axTLS/ssl/test/ssltest.c.bak | 1940 ++++++ libs/nixio/axTLS/ssl/test/test_axssl.sh | 163 + libs/nixio/axTLS/ssl/test/thawte.x509_ca | Bin 0 -> 811 bytes libs/nixio/axTLS/ssl/test/verisign.x509_ca | Bin 0 -> 668 bytes libs/nixio/axTLS/ssl/test/verisign.x509_ca.pem | 16 + libs/nixio/axTLS/ssl/test/verisign.x509_my_cert | Bin 0 -> 1095 bytes .../nixio/axTLS/ssl/test/verisign.x509_my_cert.pem | 25 + libs/nixio/axTLS/ssl/tls1.c | 2057 ++++++ libs/nixio/axTLS/ssl/tls1.h | 289 + libs/nixio/axTLS/ssl/tls1_clnt.c | 386 ++ libs/nixio/axTLS/ssl/tls1_svr.c | 476 ++ libs/nixio/axTLS/ssl/version.h | 1 + libs/nixio/axTLS/ssl/x509.c | 502 ++ libs/nixio/axTLS/www/bin/.htaccess | 2 + libs/nixio/axTLS/www/favicon.ico | Bin 0 -> 22486 bytes libs/nixio/axTLS/www/index.html | 7106 ++++++++++++++++++++ libs/nixio/axTLS/www/lua/download.lua | 75 + libs/nixio/axTLS/www/lua/env.lua | 26 + libs/nixio/axTLS/www/lua/overview.lp | 64 + libs/nixio/axTLS/www/lua/prepara_sql2.lua | 31 + libs/nixio/axTLS/www/lua/test_conc.lua | 38 + libs/nixio/axTLS/www/lua/test_cookies.lp | 13 + libs/nixio/axTLS/www/lua/test_cookies.lua | 14 + libs/nixio/axTLS/www/lua/test_err.lua | 4 + libs/nixio/axTLS/www/lua/test_fs.lua | 23 + libs/nixio/axTLS/www/lua/test_htk.lua | 22 + libs/nixio/axTLS/www/lua/test_lib.lua | 31 + libs/nixio/axTLS/www/lua/test_main.html | 127 + libs/nixio/axTLS/www/lua/test_main.lp | 31 + libs/nixio/axTLS/www/lua/test_main.lua | 46 + libs/nixio/axTLS/www/lua/test_session.lua | 43 + libs/nixio/axTLS/www/lua/test_sql.lua | 13 + libs/nixio/axTLS/www/lua/test_sql2.lua | 24 + libs/nixio/axTLS/www/lua/test_variables.lp | 14 + libs/nixio/axTLS/www/test_dir/bin/.htaccess | 1 + libs/nixio/axTLS/www/test_dir/no_http/.htaccess | 1 + libs/nixio/axTLS/www/test_dir/no_http/.htpasswd | 2 + libs/nixio/axTLS/www/test_dir/no_http/index.html | 6 + libs/nixio/axTLS/www/test_dir/no_ssl/.htaccess | 1 + libs/nixio/axTLS/www/test_dir/no_ssl/index.html | 6 + 218 files changed, 52420 insertions(+), 5 deletions(-) create mode 100644 libs/nixio/axTLS/.prepared create mode 100644 libs/nixio/axTLS/Makefile create mode 100644 libs/nixio/axTLS/README create mode 100644 libs/nixio/axTLS/_stage/libaxtls.a create mode 120000 libs/nixio/axTLS/_stage/libaxtls.so.1 create mode 100755 libs/nixio/axTLS/_stage/libaxtls.so.1.2 create mode 100644 libs/nixio/axTLS/bindings/Config.in create mode 100644 libs/nixio/axTLS/bindings/Makefile create mode 100644 libs/nixio/axTLS/bindings/README create mode 100644 libs/nixio/axTLS/bindings/csharp/Makefile create mode 100644 libs/nixio/axTLS/bindings/csharp/axTLS.cs create mode 100755 libs/nixio/axTLS/bindings/generate_SWIG_interface.pl create mode 100755 libs/nixio/axTLS/bindings/generate_interface.pl create mode 100644 libs/nixio/axTLS/bindings/java/Makefile create mode 100644 libs/nixio/axTLS/bindings/java/SSL.java create mode 100644 libs/nixio/axTLS/bindings/java/SSLCTX.java create mode 100644 libs/nixio/axTLS/bindings/java/SSLClient.java create mode 100644 libs/nixio/axTLS/bindings/java/SSLReadHolder.java create mode 100644 libs/nixio/axTLS/bindings/java/SSLServer.java create mode 100644 libs/nixio/axTLS/bindings/java/SSLUtil.java create mode 100644 libs/nixio/axTLS/bindings/lua/Makefile create mode 100644 libs/nixio/axTLS/bindings/perl/Makefile create mode 100644 libs/nixio/axTLS/bindings/vbnet/Makefile create mode 100644 libs/nixio/axTLS/bindings/vbnet/axTLSvb.vb create mode 100644 libs/nixio/axTLS/config/.config create mode 100644 libs/nixio/axTLS/config/Config.in create mode 100755 libs/nixio/axTLS/config/JMeter.jmx create mode 100644 libs/nixio/axTLS/config/Rules.mak create mode 100755 libs/nixio/axTLS/config/axhttpd.aip create mode 100644 libs/nixio/axTLS/config/axtls.RES create mode 100644 libs/nixio/axTLS/config/axtls.rc create mode 100644 libs/nixio/axTLS/config/config.h create mode 100644 libs/nixio/axTLS/config/linuxconfig create mode 100644 libs/nixio/axTLS/config/makefile.conf create mode 100644 libs/nixio/axTLS/config/makefile.dotnet.conf create mode 100644 libs/nixio/axTLS/config/makefile.java.conf create mode 100644 libs/nixio/axTLS/config/makefile.post create mode 100644 libs/nixio/axTLS/config/scripts/config/Kconfig-language.txt create mode 100644 libs/nixio/axTLS/config/scripts/config/Makefile create mode 100644 libs/nixio/axTLS/config/scripts/config/conf.c create mode 100644 libs/nixio/axTLS/config/scripts/config/confdata.c create mode 100644 libs/nixio/axTLS/config/scripts/config/expr.c create mode 100644 libs/nixio/axTLS/config/scripts/config/expr.h create mode 100644 libs/nixio/axTLS/config/scripts/config/lex.zconf.c_shipped create mode 100644 libs/nixio/axTLS/config/scripts/config/lkc.h create mode 100644 libs/nixio/axTLS/config/scripts/config/lkc_proto.h create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/BIG.FAT.WARNING create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/checklist.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/colors.h create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/dialog.h create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/inputbox.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/menubox.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/msgbox.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/textbox.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/util.c create mode 100644 libs/nixio/axTLS/config/scripts/config/lxdialog/yesno.c create mode 100644 libs/nixio/axTLS/config/scripts/config/mconf.c create mode 100644 libs/nixio/axTLS/config/scripts/config/menu.c create mode 100755 libs/nixio/axTLS/config/scripts/config/mkconfigs create mode 100644 libs/nixio/axTLS/config/scripts/config/symbol.c create mode 100644 libs/nixio/axTLS/config/scripts/config/util.c create mode 100644 libs/nixio/axTLS/config/scripts/config/zconf.l create mode 100644 libs/nixio/axTLS/config/scripts/config/zconf.tab.c_shipped create mode 100644 libs/nixio/axTLS/config/scripts/config/zconf.tab.h_shipped create mode 100644 libs/nixio/axTLS/config/scripts/config/zconf.y create mode 100644 libs/nixio/axTLS/config/win32config create mode 100644 libs/nixio/axTLS/crypto/.depend create mode 100644 libs/nixio/axTLS/crypto/Makefile create mode 100644 libs/nixio/axTLS/crypto/aes.c create mode 100644 libs/nixio/axTLS/crypto/bigint.c create mode 100644 libs/nixio/axTLS/crypto/bigint.h create mode 100644 libs/nixio/axTLS/crypto/bigint_impl.h create mode 100644 libs/nixio/axTLS/crypto/crypto.h create mode 100644 libs/nixio/axTLS/crypto/crypto_misc.c create mode 100644 libs/nixio/axTLS/crypto/hmac.c create mode 100644 libs/nixio/axTLS/crypto/md2.c create mode 100644 libs/nixio/axTLS/crypto/md5.c create mode 100644 libs/nixio/axTLS/crypto/rc4.c create mode 100644 libs/nixio/axTLS/crypto/rsa.c create mode 100644 libs/nixio/axTLS/crypto/sha1.c create mode 100644 libs/nixio/axTLS/docsrc/Makefile create mode 100644 libs/nixio/axTLS/docsrc/axTLS.dox create mode 100644 libs/nixio/axTLS/docsrc/doco_footer.html create mode 100644 libs/nixio/axTLS/docsrc/images/axolotl.jpg create mode 100644 libs/nixio/axTLS/docsrc/images/tsbasbw.gif create mode 100644 libs/nixio/axTLS/httpd/Config.in create mode 100644 libs/nixio/axTLS/httpd/Makefile create mode 100644 libs/nixio/axTLS/httpd/axhttp.h create mode 100644 libs/nixio/axTLS/httpd/axhttpd.c create mode 100644 libs/nixio/axTLS/httpd/htpasswd.c create mode 100755 libs/nixio/axTLS/httpd/kepler-1.1-snapshot-20070521-1825.tar.gz create mode 100644 libs/nixio/axTLS/httpd/kepler.patch create mode 100644 libs/nixio/axTLS/httpd/proc.c create mode 100644 libs/nixio/axTLS/httpd/tdate_parse.c create mode 100644 libs/nixio/axTLS/samples/Config.in create mode 100644 libs/nixio/axTLS/samples/Makefile create mode 100644 libs/nixio/axTLS/samples/c/Makefile create mode 100644 libs/nixio/axTLS/samples/c/axssl.c create mode 100644 libs/nixio/axTLS/samples/csharp/Makefile create mode 100644 libs/nixio/axTLS/samples/csharp/axssl.cs create mode 100644 libs/nixio/axTLS/samples/java/Makefile create mode 100644 libs/nixio/axTLS/samples/java/axssl.java create mode 100644 libs/nixio/axTLS/samples/java/manifest.mf create mode 100644 libs/nixio/axTLS/samples/lua/Makefile create mode 100755 libs/nixio/axTLS/samples/lua/axssl.lua create mode 100644 libs/nixio/axTLS/samples/perl/Makefile create mode 100755 libs/nixio/axTLS/samples/perl/axssl.pl create mode 100644 libs/nixio/axTLS/samples/vbnet/Makefile create mode 100644 libs/nixio/axTLS/samples/vbnet/axssl.vb create mode 100644 libs/nixio/axTLS/ssl/.depend create mode 100644 libs/nixio/axTLS/ssl/BigIntConfig.in create mode 100644 libs/nixio/axTLS/ssl/Config.in create mode 100644 libs/nixio/axTLS/ssl/Makefile create mode 100644 libs/nixio/axTLS/ssl/asn1.c create mode 100644 libs/nixio/axTLS/ssl/cert.h create mode 100644 libs/nixio/axTLS/ssl/crypto_misc.h create mode 100644 libs/nixio/axTLS/ssl/gen_cert.c create mode 100644 libs/nixio/axTLS/ssl/loader.c create mode 100644 libs/nixio/axTLS/ssl/openssl.c create mode 100644 libs/nixio/axTLS/ssl/os_port.c create mode 100644 libs/nixio/axTLS/ssl/os_port.h create mode 100644 libs/nixio/axTLS/ssl/p12.c create mode 100644 libs/nixio/axTLS/ssl/private_key.h create mode 100644 libs/nixio/axTLS/ssl/ssl.h create mode 100644 libs/nixio/axTLS/ssl/test/Makefile create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.ca_key.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.ca_x509.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.ca_x509.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.device_key create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.device_key.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.encrypted.p8 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.encrypted_pem.p8 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_1024 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_1024.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_2048 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_2048.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_4096 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_4096.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_512 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_512.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_aes128.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.key_aes256.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.noname.p12 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.unencrypted.p8 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.unencrypted_pem.p8 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.withCA.p12 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.withoutCA.p12 create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_1024.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_1024.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_2048.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_2048.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_4096.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_4096.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_512.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_512.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_aes128.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_aes256.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_bad_after.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_bad_before.pem create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_device.cer create mode 100644 libs/nixio/axTLS/ssl/test/axTLS.x509_device.pem create mode 100644 libs/nixio/axTLS/ssl/test/datatest.c create mode 100644 libs/nixio/axTLS/ssl/test/datatest.c.old create mode 100644 libs/nixio/axTLS/ssl/test/deutsche_telecom.x509_ca create mode 100644 libs/nixio/axTLS/ssl/test/equifax.x509_ca create mode 100755 libs/nixio/axTLS/ssl/test/gnutls.cer create mode 100755 libs/nixio/axTLS/ssl/test/header_issue.dat create mode 100755 libs/nixio/axTLS/ssl/test/killopenssl.sh create mode 100755 libs/nixio/axTLS/ssl/test/make_certs.sh create mode 100644 libs/nixio/axTLS/ssl/test/microsoft.x509_ca create mode 100644 libs/nixio/axTLS/ssl/test/microsoft.x509_ca.pem create mode 100755 libs/nixio/axTLS/ssl/test/ms_iis.cer create mode 100644 libs/nixio/axTLS/ssl/test/perf_bigint.c create mode 100755 libs/nixio/axTLS/ssl/test/socgen.cer create mode 100644 libs/nixio/axTLS/ssl/test/ssltest.c create mode 100644 libs/nixio/axTLS/ssl/test/ssltest.c.bak create mode 100755 libs/nixio/axTLS/ssl/test/test_axssl.sh create mode 100644 libs/nixio/axTLS/ssl/test/thawte.x509_ca create mode 100644 libs/nixio/axTLS/ssl/test/verisign.x509_ca create mode 100644 libs/nixio/axTLS/ssl/test/verisign.x509_ca.pem create mode 100644 libs/nixio/axTLS/ssl/test/verisign.x509_my_cert create mode 100644 libs/nixio/axTLS/ssl/test/verisign.x509_my_cert.pem create mode 100755 libs/nixio/axTLS/ssl/tls1.c create mode 100755 libs/nixio/axTLS/ssl/tls1.h create mode 100644 libs/nixio/axTLS/ssl/tls1_clnt.c create mode 100644 libs/nixio/axTLS/ssl/tls1_svr.c create mode 100644 libs/nixio/axTLS/ssl/version.h create mode 100644 libs/nixio/axTLS/ssl/x509.c create mode 100644 libs/nixio/axTLS/www/bin/.htaccess create mode 100644 libs/nixio/axTLS/www/favicon.ico create mode 100755 libs/nixio/axTLS/www/index.html create mode 100644 libs/nixio/axTLS/www/lua/download.lua create mode 100644 libs/nixio/axTLS/www/lua/env.lua create mode 100644 libs/nixio/axTLS/www/lua/overview.lp create mode 100644 libs/nixio/axTLS/www/lua/prepara_sql2.lua create mode 100644 libs/nixio/axTLS/www/lua/test_conc.lua create mode 100644 libs/nixio/axTLS/www/lua/test_cookies.lp create mode 100644 libs/nixio/axTLS/www/lua/test_cookies.lua create mode 100644 libs/nixio/axTLS/www/lua/test_err.lua create mode 100644 libs/nixio/axTLS/www/lua/test_fs.lua create mode 100644 libs/nixio/axTLS/www/lua/test_htk.lua create mode 100644 libs/nixio/axTLS/www/lua/test_lib.lua create mode 100644 libs/nixio/axTLS/www/lua/test_main.html create mode 100644 libs/nixio/axTLS/www/lua/test_main.lp create mode 100644 libs/nixio/axTLS/www/lua/test_main.lua create mode 100644 libs/nixio/axTLS/www/lua/test_session.lua create mode 100644 libs/nixio/axTLS/www/lua/test_sql.lua create mode 100644 libs/nixio/axTLS/www/lua/test_sql2.lua create mode 100644 libs/nixio/axTLS/www/lua/test_variables.lp create mode 100644 libs/nixio/axTLS/www/test_dir/bin/.htaccess create mode 100644 libs/nixio/axTLS/www/test_dir/no_http/.htaccess create mode 100644 libs/nixio/axTLS/www/test_dir/no_http/.htpasswd create mode 100644 libs/nixio/axTLS/www/test_dir/no_http/index.html create mode 100644 libs/nixio/axTLS/www/test_dir/no_ssl/.htaccess create mode 100644 libs/nixio/axTLS/www/test_dir/no_ssl/index.html diff --git a/NOTICE b/NOTICE index 18287929b..c708e5e81 100644 --- a/NOTICE +++ b/NOTICE @@ -6,3 +6,4 @@ Licensed under the Apache License, Version 2.0. Contains code from: coxpcall - Copyright 2005 - Kepler Project (www.keplerproject.org) ltn12/luasocket - Copyright 2004-2007 Diego Nehab +axTLS - Copyright 2008 Cameron Rich diff --git a/libs/nixio/.gitignore b/libs/nixio/.gitignore index 30c885354..d9c7ea012 100644 --- a/libs/nixio/.gitignore +++ b/libs/nixio/.gitignore @@ -1 +1 @@ -axTLS +src/libaxtls.a diff --git a/libs/nixio/Makefile b/libs/nixio/Makefile index 61237af44..57ff99a9e 100644 --- a/libs/nixio/Makefile +++ b/libs/nixio/Makefile @@ -40,9 +40,9 @@ compile: $(NIXIO_OBJ) mkdir -p dist$(LUA_LIBRARYDIR) cp src/nixio.so dist$(LUA_LIBRARYDIR)/nixio.so -$(AXTLS_DIR)/.prepared: $(AXTLS_FILE) - rm -rf $(AXTLS_DIR) - tar xvfz $(AXTLS_FILE) +$(AXTLS_DIR)/.prepared: + #rm -rf $(AXTLS_DIR) + #tar xvfz $(AXTLS_FILE) cp axtls-config/{.config,config.h} $(AXTLS_DIR)/config touch $@ @@ -52,4 +52,4 @@ src/libaxtls.a: $(AXTLS_DIR)/.prepared clean: luaclean rm -f src/*.o src/*.so src/*.a - rm -rf $(AXTLS_DIR) + rm -f $(AXTLS_DIR)/.prepared diff --git a/libs/nixio/axTLS/.prepared b/libs/nixio/axTLS/.prepared new file mode 100644 index 000000000..e69de29bb diff --git a/libs/nixio/axTLS/Makefile b/libs/nixio/axTLS/Makefile new file mode 100644 index 000000000..02c33d248 --- /dev/null +++ b/libs/nixio/axTLS/Makefile @@ -0,0 +1,181 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +-include config/.config + +ifneq ($(strip $(HAVE_DOT_CONFIG)),y) +all: menuconfig +else +all: target +endif + +include config/makefile.conf + +target : $(STAGE) $(TARGET) + +# VERSION has to come from the command line +RELEASE=axTLS-$(VERSION) + +# standard version +target: + $(MAKE) -C crypto + $(MAKE) -C ssl +ifdef CONFIG_AXHTTPD + $(MAKE) -C httpd +endif +ifdef CONFIG_BINDINGS + $(MAKE) -C bindings +endif +ifdef CONFIG_SAMPLES + $(MAKE) -C samples +endif + +$(STAGE) : ssl/version.h + @mkdir -p $(STAGE) + +# create a version file with something in it. +ssl/version.h: + @echo "#define AXTLS_VERSION \"(no version)\"" > ssl/version.h + +$(PREFIX) : + @mkdir -p $(PREFIX)/lib + @mkdir -p $(PREFIX)/bin + +release: + $(MAKE) -C config/scripts/config clean + -$(MAKE) clean + -@rm config/*.msi config/*.back.aip config/config.h config/.config* + -@rm www/index.20* + -@rm -fr $(STAGE) + @echo "#define AXTLS_VERSION \"$(VERSION)\"" > ssl/version.h + cd ../; tar cvfz $(RELEASE).tar.gz --wildcards-match-slash --exclude .svn axTLS; cd -; + +docs: + $(MAKE) -C docsrc doco + +# build the Win32 demo release version +win32_demo: + @echo "#define AXTLS_VERSION \"$(VERSION)\"" > ssl/version.h + $(MAKE) win32releaseconf + +install: $(PREFIX) all + cp --no-dereference $(STAGE)/libax* $(PREFIX)/lib + chmod 755 $(PREFIX)/lib/libax* +ifdef CONFIG_SAMPLES + install -m 755 $(STAGE)/ax* $(PREFIX)/bin +endif +ifdef CONFIG_HTTP_HAS_AUTHORIZATION + install -m 755 $(STAGE)/htpasswd $(PREFIX)/bin +endif +ifdef CONFIG_PLATFORM_CYGWIN + install -m 755 $(STAGE)/cygaxtls.dll $(PREFIX)/bin +endif +ifdef CONFIG_PERL_BINDINGS + install -m 755 $(STAGE)/axtlsp.pm `perl -e 'use Config; print $$Config{installarchlib};'` +endif + @mkdir -p -m 755 $(PREFIX)/include/axTLS + install -m 644 crypto/*.h $(PREFIX)/include/axTLS + install -m 644 ssl/*.h $(PREFIX)/include/axTLS + -rm $(PREFIX)/include/axTLS/cert.h + -rm $(PREFIX)/include/axTLS/private_key.h + install -m 644 config/config.h $(PREFIX)/include/axTLS + +installclean: + -@rm $(PREFIX)/lib/libax* > /dev/null 2>&1 + -@rm $(PREFIX)/bin/ax* > /dev/null 2>&1 + -@rm $(PREFIX)/bin/axhttpd* > /dev/null 2>&1 + -@rm `perl -e 'use Config; print $$Config{installarchlib};'`/axtlsp.pm > /dev/null 2>&1 + +test: + cd $(STAGE); ssltest; ../ssl/test/test_axssl.sh; cd -; + +# tidy up things +clean:: + @cd crypto; $(MAKE) clean + @cd ssl; $(MAKE) clean + @cd httpd; $(MAKE) clean + @cd samples; $(MAKE) clean + @cd docsrc; $(MAKE) clean + @cd bindings; $(MAKE) clean + +# --------------------------------------------------------------------------- +# mconf stuff +# --------------------------------------------------------------------------- + +CONFIG_CONFIG_IN = config/Config.in +CONFIG_DEFCONFIG = config/defconfig + +config/scripts/config/conf: config/scripts/config/Makefile + $(MAKE) -C config/scripts/config conf + -@if [ ! -f config/.config ] ; then \ + cp $(CONFIG_DEFCONFIG) config/.config; \ + fi + +config/scripts/config/mconf: config/scripts/config/Makefile + $(MAKE) -C config/scripts/config ncurses conf mconf + -@if [ ! -f config/.config ] ; then \ + cp $(CONFIG_DEFCONFIG) .config; \ + fi + +cleanconf: + $(MAKE) -C config/scripts/config clean + @rm -f config/.config + +menuconfig: config/scripts/config/mconf + @./config/scripts/config/mconf $(CONFIG_CONFIG_IN) + +config: config/scripts/config/conf + @./config/scripts/config/conf $(CONFIG_CONFIG_IN) + +oldconfig: config/scripts/config/conf + @./config/scripts/config/conf -o $(CONFIG_CONFIG_IN) + +default: config/scripts/config/conf + @./config/scripts/config/conf -d $(CONFIG_CONFIG_IN) > /dev/null + $(MAKE) + +randconfig: config/scripts/config/conf + @./config/scripts/config/conf -r $(CONFIG_CONFIG_IN) + +allnoconfig: config/scripts/config/conf + @./config/scripts/config/conf -n $(CONFIG_CONFIG_IN) + +allyesconfig: config/scripts/config/conf + @./config/scripts/config/conf -y $(CONFIG_CONFIG_IN) + +# The special win32 release configuration +win32releaseconf: config/scripts/config/conf + @./config/scripts/config/conf -D config/win32config $(CONFIG_CONFIG_IN) > /dev/null + $(MAKE) + +# The special linux release configuration +linuxconf: config/scripts/config/conf + @./config/scripts/config/conf -D config/linuxconfig $(CONFIG_CONFIG_IN) > /dev/null + $(MAKE) diff --git a/libs/nixio/axTLS/README b/libs/nixio/axTLS/README new file mode 100644 index 000000000..c8926d9bf --- /dev/null +++ b/libs/nixio/axTLS/README @@ -0,0 +1,3 @@ + +See www/index.html for the README, CHANGELOG, LICENSE and other notes. + diff --git a/libs/nixio/axTLS/_stage/libaxtls.a b/libs/nixio/axTLS/_stage/libaxtls.a new file mode 100644 index 0000000000000000000000000000000000000000..e1ea96e452f4bc2782f12522a9ddb6db8b023feb GIT binary patch literal 110900 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

~d7 z&2?l@v~l)^$It{_Ic1I!tMfFhOhBqg&5%|w3s2YL&J)Z}D^c6?lPyX+@lDM(8>^kt zvn={6r8H{QR(k%=aE(9WJQ_;>6A^oSN1(U1h&rU_>v8UeBlaJ<98(((&}E9-dB7WT z{2rA$0x^5WGkMjma!gVD^VC2qy4hhHR^MG%`5Q&?Gc;eDKJA`0tJL6dPoT}_&goe_ z1oTstns2#~{|GN0xyQNm*QpFdtm^x;P|CedEOse5udjYk4O)W3XurVzP>W{i)KB0? z)7!BqNGpZm4>DBf8h^b9cRkN>JvvK&Q`I9b{r#Tb8U4j9GKfoq+>FYzZ(ES;f$~Os zZV9ehcd`L%ox89GQ4%;|v2T1FzR>i31U75PiFA9Nmb(99nKa~D+Ru@!6=TVX}?(Z`>G~_Uju%MZdOrgUi?w&j<}$E%qI=lRbf5 zHmQq(-a(yeTp*HwB(l_?`($v=Nz|TZf2f&>xkb-AsfFLmKzni6H~s<4AyU>Xy~m@! z0+sA`?n7f>yFjd*l!6*O6E$dmr~?(JSz6o;*ulseIx}ROq_P1^v%67$9on%@G_^@u zPWY~~v|}&PCQ;5us{O~!ntp=q2kAMKv9U`O#*WxqHNR9X=OH;i){3!x(avP$3AAH0 z#jOpi!dYk&t*n&Yndn&{KdrAoQlyne*tg9_AG(3nIhx_RlSsH`D%Is0r@6Mz#6qS~ z&uh+g1+kn*381Fk4ZBP9Im!Mvi601gXSUSKXh4bTQ$s%(TRn%z*>_A&w(lrs)r!hn zg}CVH$$BfR*B<@#NXlol(!My&qwk`$)0{`W!CF?A$yk{q-GE&aYA((Lny-_!V)V;c ze)+-l(Km!rMtOBCLhr}$={(BnCpd(4CF})4QtdY0rCjZ#%AqnS!ThWE%wBs@*U<1 zrt?lTFWKF&&;KsOI?k&{B*{WANb=M_rXk8@-+|c()39?LDell+7MFfBvg!R7Ixauh}%iyd_+HPMGHqTf!l?h%IEa9 zwJ$K8OEBt-QXWw9U>VfNk(x|a?#s>P#wG`EPxj!bXmK%&9YSeGm`jWCh~{&j=r0A@ ztn>62L++$GcC9yjDk1VtANB;$u{E>~C;+`x>fL$-+NySew#iBJCr>DgHe!1qa!QXi zfcGtGPQ*VM+qT#v4yAlfrC?K6YhD^wWS@V_k{0N<)$Ai`nnC4e_S38ik!qf{Z<`bC zKh%BDd$L$zCz=a;7R?VudDC1$2P-i8jl1C;|3M7GRJAnQl={mY{o|)d6Gp5kpcb5+ zreAOyNMikIzDGZ$=`Yhf^6yMIG>@EP-*J1g{(7Ltq92Il979aB-&q)5Fq*p4yj%&R zvisFwdXCW$xEAsaT^aV&VC*8XtsDmDGkK1B>!M~w*D+6XtiB! zX%q6xChZ-Q-*YwlDLox!{y^1nyn>Z_K*KS5$u+FVlg&j=kL4FDw~Kce)_xA4{jkYA zYeVWHOy25=QLZ06`in-}ppjH-*lpi-2iFl^M~Z!GzTdv>_T)yF;|f2t@XL%l>?_~* zTRgeicN{VLcbOx@zN1Hf?+VOh^p~Kf9hizl%0BJy$CM4KIfIcwNNiLd75OGYP_eU6 zuQkv+ZK&CBaPIn(XfoHA1dd}qk!!=+k@`jR38K0HRKfHl?6P7$F$)t~|J$LN7A-nQ zpl$zN4&ri+lQ$S4OFz}=hE1gVJ6DT zW*zB{o?Bz}g(UJJfdy&fBcOvGYPa3P>czeTlU>Rx6HjVaU^X=V1DbC5`so?MV->Sl zHr?1T11i8s-@c>QiebhRT!WDZksrtC-ar0*JTPc@Ztx(?m1$H%_Q>OkEq?4q(;hE9|fK1a=0)*u{RV!BA(!iwC=xR+DLlUs98gw zpetxl0@6qcWn}aRPRAdwno~@x6-#>^t^R+neCr4ULF)>qDxT>rZ3K#n5m1XC53fNz=k_ zX5iovrXwHNg6C+KJ*X+?J`Wmt&PTdc)Az1?(PQ7y>e63z2Un3o#*F=Qnn|mH&(L{3 zg4fI*2EJTH=n?zI=Ro2PW-;B&cyvRHc6Dzzo4%aSdq zHDtFu+0uOn#Bdxl}Xl1?!9V3@|slEOk zjLUl?4ITau?K|4gJJ}2|+?5z|rFBJzJP2a~dL}FBQ%f`j%O_i#2hVLW$R`g1x~;aR;MO=%Z6;fts2=|^ux z{vO@EnAMb~TRlPdVl9}>Zpn{eEttt}$qNNfDX4*dERkZ;u%=jjf<)*%(lgZPk7cMs z@SdTC=3%vRC&->B8D)B`i;PS>!FfiiS(ghnB&8f>VbwTe8PT0{a@FtXQ7r>hEOY1u z8|6td81Hr1-1_QeVtfGI1j;@+a!+@EKOL^y2TB(jn$2Y`a!gn$&UkziDbVv5aY*zo z{b1xK7RBG=rlndmpb5?+&V8<&w^uEU>JzkV!G_EHLam}{W^})j zWu`X==fed)sL`mHlOW>CJ?`MVWtilznS_!Yp!%WAXr2s$1^z?-aBKf%<}8*O?11dJ1mgwI*P0CIj0LjhA_#!5D6G&up9uc4Di6EwU?O$@CN z6T^(5)yRz2Wiv<_LC+Q8d>N)pKr%@mWs|y0=Ph0{~m4S3I{A^ewXAq=c!r^ncH)jZsDzCKRv9=&-1Z*!DG7IK0hDOJ~1q$I6S-^;xU=$h`dQS7?k;L2`$|U zzgIs+a}gdgubn0txQ|L!cO$(q+5k7(AjNcoZ2%g})bgyjih$IH4t-xmtYMkt-H2h4_Q(p`R$9(RgyC{M6Y* zWSkRo@px|$fOs1cJgFnrTW63ENNzy{k&O26M^4{I0P=~o2bEd_HHu7Deoi+v(V#QQdjqA*&Ysn=Gh~z+JMWglHro_s z1_7wYfHglggZyyR9w+edTJq{K!ffP?w?6~xHlwzlUNB1xj8jAdW4R(*Vj1R zbc)E^G3E0K$Bz&$=fes^Al$$BEFGP&`%gGScP({T$uVUk-IFe`N%z9rG2ZZP;%Cw5 zLgG*61hfrH?o#{_&P7ZRb9JVC1#ZrZ`UR!=&&DU6MfvkC0H5JR{v%8o3jPHhoxwAa zu%N}%vEVlIgsJ@0bVYno{#;;`zu9fdFpvvP0cKhc!p#^(`CA74p6<-F@y@`B_}?-m zVUADtX4z4cG=~u}gA0ew0MfBObNPn?GuIu(%p6C*in7U+q3PBj8swMg{+(yws6G=Q z`8uBWG=4pwkasm&&H+musIyh;P+zALRX@1=D3Srm*Z;I2^18FgEs=FbkcZ5szeOe zgulVk`4MoUU(D&*+y>a@IP|~e^rBI+IS#O`9M9m56-F_+Wf~m($PY70AYF ziog#W`V<(YfYk_C0H`h?yAXI)A1`milmM&-u=nj@(}HY26b!IAfj6P$`!6wpQIPMw zX}anyxM{|WIqtaybJgiycY(rQkcP1eb}d}OEn{#S8L$}uTMcB5;_I8r7}Qt*S;hDl z*v|l8K5p)#?0;iMBWBtFj7bJHUZJ{yBeh3k5Z0)fdxtX{92^5NWx|Ml*=5(4R zT&6GNdfZIEfa`HH{;7o1JL{qzTj>Ul=;icRQDcH5{9^nG`bXm6xm*boJ!K{6eG;A_ z;eU~ES zPpF7KL&EDMT&|a`lo=e+)9#2!XE-SZIKt)rc)5hj`Q}JC4YeX2O~UEXO5k%O{1OSj zPr~K;{gs5%vx!LOPZBPt^OS_k{B%mVEdK@PAyGIgugvEX376CV9|@QFc~Zh<`Zpw8 zPX9d#m+43G`3mtvEQ|7HO1RAb^%5?RYhDSL>Aw{RUoYWud+3vJxn7d^{E7IyRN`}F z9Q;xVm(!Ul;c|b;m+%ZJ{RI*(%fCdzWqGcUaG5@x1|c}&pNc5tJTVTgNw`Cz-zwp9 z|81A>izNCKK3}AK=~+zVneKXnp5UZUS9;W9rzlW=;Z73u$0!e#ou#lc%8T&~}} z5-z9nhJ?%Qty{vW$qRlylyIsdfuEFcIh`TYdEiKHGJZ}R{C#PjCe!~&nzzXK5eb+3 z(N;-+W*~3DXOD!-_4^yCy~*wQsMH?h`lU30JPw>B)<0DLXGsiPcxLCKK=R0yD9kFh)%!G?| zi_IonJn!x|;o^BWjpr}Y7wa6F2^Y`7%S^a<4z4rd;yL(X6E2>E_nUE2esF1gK1{)k zgpcc)CR{x4-eSi2_$}ll(h<+Q+fDQj^LE)~!o~A$p9vSwwalDfMZxmYY9D)n?#tet z%VIXS$imiFe|N5vIyHB>?3#;b?z5D6cqsrcujzls$6b2@9=s#ZF zVgl_M$IcRbX0A=egI0<}_OP`0lZliOixkhoiZ^iSdamI+fyN58% z_j2%BFr{?U56R-71W5H6x>EB^XV8b0cd*i!W`e2khd9XW4j{9;#|mrnEb@lygg zu!rOEcy$4%&nPj?hr@lV75%;OufjSRJLMpDtu3uNJ*$RC3Bias@!#6i(7bk6&pV1j zd|X*nyyD6lZd9{siQ*PO=+6r zKkyafI}w-ipM*cziTJGTNC8AkPF&sGZ_dAy=P%03BH_n;p9{=fCZg@*oj}NmZmEt$ ze1YErjCe6slhVb3I)uqCL%xB(7=MGsU(5^CVWJoL3wSX+gYlon*G!r?A@NVkKq9{2 zzXF($B`0NXm60ICbsY1vseyzl$~QR9r^ExotP0;?l2Fncw5Jnw z+?y+Ke$Trz+jBJQP>NfKzrpYlo{o4%noY!TH0BLPzY@5-S9mcI!`+L&!RSN4*w=aqvgu;5*{ryW`-`#lc?!K1>?bqAf;#chdL?@rgEV{3(q8g^h z2O3AmJ`pp8O|hAg^b&SkQe9kpr&8jFHDj`k%?)B>Ck;lNON@lf*3_~0iSm;p-9&Rm zu||-{>y2>II|9Fu6{_uOFCDiFYg7)Yg-^;j-pXt5Pds?p+-C5EHEV3 zjPv|xzCn&?=}+h-8i&aVd@-kQGT8)`Ll2wof)Kl;W3gGUg}tPxDOsJ^hd|TxOzinj!L}Q zkpg?rI$3wod@#6j@MG-{xW#c=O@Ed7`Kxj|o(SnR(FoyizMF0NB(oFgxX zztl#dIPW0x5m(Z+vWxeO`BmdjL1M(0shG+w9B7m?gJ1A3V0uP1^F(2b`65szC!|}N z(TeyZIG&3u7ZFE{OBc`o%bu}3apHe&E)Y-MoL9VOED`@(1GqY!*TG~*QEuUn2M_ZM zX?9L!HdK{+-WQlv(^|BT>o$EP!RTiLCw|1-LGVNEU@-b6aq#7FaP|qIi9YF@LW57$ z`T4Fd{d^DmHj(i~qA&X9kTM7SkfXjT&$$a3dOE&Q!l~_wIovNKoSs|-PS056D18~< z&QJrF=gQ3zE~noq;c_}%aqu1qm(v%v8O`PWHt#@Y{1*~V>j$D-V(w1#RR02hQlgh} zF?T0=`B|k?qLuKz`&%?a8X|7KX-f(XN*lO_HawUp|+e{PpLu z86*Vma@p1R4vcf3v2O$G7w6?Ry3Wg(0^BQgE#nS&+wrMl(QIb5~ zkDn8qKO06{#_bK9u&*5v-4}wn1Y`xQ(QD0J^C3-VbMT-Bqj&J)g}hwD3VNOvxro8^ zh90aF1@lJWnv9L9)8?*ZvktG|8^$7GK8H4W@Yd@Jbq8_MlWoXH?{UIhS&P^AF=o#A z%#1A;ZMvgRZ2s)ijgMdX{&#-2Y~}aP{huM%-`|p^pLtQi*t)fgetK)uq{}8>_spRk$9`<> zKR@%Y+JFCT@5{G;|MEZ1|9#s{+q3um=)u)nSDd`_tq1(J58Xen{8rJo@6A|$+jGSS zzp*7@n;*Kmb>Z=6AKUfbr4#0yRo*bJe(3XW z{QVPk-K=lk@!HgmvhW{HINrQFIpxW}%&cwvVeoBVWO?_~FP^$axiIIW54NS2+|v2Z zZ|#4cn45w@MBljH{bHRb1gs%FpVS)5`b8f=LoV!7i7hyE7CRa#5ru`)fn@c#{ zf6>qB?^v;Syw)eNcjI!!%UP7)2BL=}T@ZEu;&+|?)9i--h3iI9Ug|Tli#I3FrpXpu zrg32_rj(SJt%y14rN9WMwHn!dV#-&9NotNk6YxuE{}U?vuU;GGbj|Ci0_Z;liH zNwYv%s;B2gePJ+C5x5^WWX!MT{7u2p zTcZ1S&grRm^bVA8LWddqCiL{iU5o^BmT(b{>%YQ2IO$Ec{?F4bNjRxKvDAJ;5P!NbT;#JsNFC}lOwFZ7fr$^{Mfe>mD!$xIrFB^nLGH0KfiDaAN?vQ z+fSzSsb7<<#a!|_o&r%7x3Z1q3Os;DQtl)=;(+@-XH$_N{YM zEtqJgCg8($`~!QoRQk}IE7i)rQm3cenLXRU@!Xodu+p&|MmfnY$uq98Eo6t~1sITW zbiw#Q#04W5pQxcnthEo`@iLAyxd8Sa>vWCn9NKAVL)0h2&z|qHw5g#d66SAfkLbPn zM-<%R8ruO1GFsOhelFc|ObuHd<8vYmGcE?p zeZRN!m%KZF|c`JVt%Usn@pzP|geJDJv zn6vUv8k_uxhjC)mf({c^9qQOU>ao4x0H1q`)xr}(4ofZ>eH79AT)YWDn0v#|j^D6zY~7$Jj*f2{^nb6zm)iAvhPIshBl+@+g&91vn|I_{NJ~4?WO{t z_Q&>t#p$6(u3MCL>M` zp#h+AK=#gc2Y!EHX~@*pI2;%W>_px|2KS!z;wM}NRKcAF#eRv2Bk8xgPiNw=#6)8E+rN{U zFw$s-07Mn^{rZEG)Qr##kOh;(*jExNmJ*v$NsR3kvIyOO!FRovhbXicoIp;y&mP-{ z4oU5RRl@X~6a5qnX|*`}m^7S5=ieWGb`-1jbw|H_FO24OID18{pUZ0fqQajq$EO6S zr(KYe{qE<^K1&#Ou;}xnmgCM2*1OTJhBEoO-h1jnd|#}OC*C0v|J`%1-$03@O2;m~ z;qjxC3BG{#+_@~>%PXJQ@s#D*U@0FKDYvcp**GcX?IPt9m;LfRiS3<8ld;{9`N0ds zHYLgw$nG@OL=!J;wBd`Y_;f1c{56q&PUj1+Q2J;P@BZY#K`H=@NIg4>@$Bqd^{X+K zmn5zYZjo>^B*79cp8v<4Qo`Fs!mf+Pf1*=EyX?pPm**XW(nZ#7I*H=^-(GzGAHvUG za12d;D0qcTNX%E~Z2rN?@N>ff9~%l*A@5!&lGbnCRvg|v4CwKpVAO&S!-n^qiQ7F! zl3->h5@VJ5{by3{V_A$}{mBRY$dso(AXP+o0tqti_b#e$pypBC`74^Xq$JS?L{Cw~g|GS!NsAwAx~U z?IjGg7TE$f#@(2yA(iY7k(EKRlm)X%M`+yVW(i?|kc>mYay~w=3-d#IIAlu*cUuE* zUZ2yV=clc(1@hA^>QlLh;?g6|BkK5P!;uS}2ULs8k?zj!ZYQXlIN)m{dWVPhM^Dl63YKeLb?Ki*DA&dWMWc3d` zp;2Q%(*)yyfn8XSITTp}f{^=q92L%0b9SvgsQLck+}FJnD?##EU>rmKoEZd$$?V|# zG`+Ji-4TwE4O|PESi|y{BY`|4;IRg0q~l^U!V`^4l5rVAMAksp^?|;rcP)90rh?;J z_?cm`&&oE2HD+^(mBqOaUzTjdst_(Lm2)l3hfL2+U_Rt}u9ewnq}8{*SP&Y)&sslL z%W~*>fP^BsI?4afc-ia^5zYISgwwpA+~vlF{n9ga7Qg=!O`fqv0_!?aJ3xl*AhH+Z zO3lUsi321>oSqrTf_<9}i9RwZL$+o3m)VTG#2QMN=-Ju~&hV14oJ`#R|Gt*OpXs@% zPJ4cex0X`J=Ix3?&4V0G^XW0h{45-IEya{Cr9sZT?htn^>F$Z!sW7GSn+<7(NR)rPIqhKv=|}MFt<1O(xI9QN!wz zimK(xY}MuR+?Y>bfv3FG&ph)g@yb1jiXD5B80-yO9mS7w1&e`v>&uWD^=}x={Mmu~7(;Ho&!i!#w-t!VX>yER;T5oG@ znnzzQf7-tFN>DGb(ClbUzN&ZQEik_3LobKX_uRM6otmIp_QAggDcTbnT~iY*osDzt z32b{0Y);R#Sv_PsIHz?j9?kx7TW;f(uEqtHMXe~`SD9;6kQ^}flWjzre~7t8F~`>@ z+!%cy=%1bwoc>AH|Ept8R0?)xl&YTA>}8kBZ(fu^_|5%%EHH|1URxLMu9~8sHkMxo z0_s-(Z2Wj*xyXOEi2>e)qbSWKsGpnN!+Z%_^jT6BL?_~p;1RCDmCA2+%Q*ODV==nS zpz#|9OLP$0cw@1MKi9;VIa#`wb(SFC%!Sg+0h(G++0ErAxtQyYqBL>&(Ogk>=KSR_ z*-vy|e_vy;k`RA`aY*=JHBUGn_+*CQA8h}EC~1rnj_C~CP`wad$pe_qAU`Q{e19}A zs{l5nYATtJo$)saGmJ}gY~Z(wc`Om>R#g`cJ~m7q{JWf!C*@4}y6g21OXA373>oK- z@tsMnE!U4z4GZwVzm}VTj-AJ)6Aa$q0?iw|3C8jB@u{zyU{-y>IK;z8@Ltw*oAJpv z_NptLIf&ZLwr|5%IbcMZ+s!^X&~CIVcvmhK2Jlid))Cq)iW<88^YN{2GL-&3u&0Gq z;^Tz}+EfcnM!))0KcEP4jXi)1t@w}zmL5*Exh_mFP_p=}xnQB6e+LMU@50(Pt~?ay zJKkeyuzTin>0ibAFdGxK(6}V~NLQrMuEtK8VQ|>j&>@ z`XYSFp|}eJrB|X^(ABGr z-^p`bqj|ACVt489VFkkzIMmB<`p(5_h(Mg!#^+u%+`9B`SRrQ=_>dtx79#A{V`#3! zceMROX@a2U{C(~FF1=^`(}B+v`^Gw|0N;nih~|49%Q91068e5`5X=Wrh~V;wH;57h zSNH8UzWewUGp7%PA>aO*SiYJstk$g>atfbPSqZ7J57hMhpyqqV9HQp~<4Rv+`ivT> zM&Df7N*o3&G_+*?a&@p8n@IdjeDH3Kzoo4O?@mJme;F%)@j1^Z(Krs?ZG3G2w8mEm zKnA<%f~-1I@@L2VcWdlDY>|e>41F7SA})^QU`gk;Mcv?5tXCOlby~6hBz@N-X3CqH zmB%_8>ru3{rUkD@$06(9Zdf8mQ4(1m?B}W3NEh#v$E=Mrv)^tk-YrHqiJ+0-i2fzL z=vC+`IOoJ-8Jz~7J#Y%U=VYe)kc&4IoQDF}U&H8mJ(W(<2LtTr$4Z96)M~+c$0iWZ z(w}t&|LCZrc(^v=>Iy#LXrg=U`{jacGyL#QqbOTAo(ccM299qo)H}9wJOln_1ILFm z>m3~&PlLbHz*AV!wctoYCis#aKKOIU08v;NWoLc}ff+D2LU0%h`THHTOb8O=0|o$* zxgRF=8JNN10YuL1lMzH|v@C!r1g063(N>1Wl_y%-AZ?FUE$N%3hMqxV%%J$#szhrq z!#4=9rFjdpAwY)x7n23`Wn|dD5-qEiwKPQiYIA08QX@()=FHz9M0%&)%P3&CjJR4r z>u{%;cV_E#eol8a(Xn;7yt9(D4Op(b;Xdm|+YKSiGiYuu)*_!HdeEsfGhdIF8!`-g z(oYZ$*G@lJ_gkMdiwsV~o^;HxwF5!#P&izW@?&}fB#n#{tU2B z#fmrA?-AwW#Z2Q@af!F?C*J)|}T*)z=iz_`F4n}`l9DGR}yetmBA`adV2mb+Z;{^{s!ZJTO52x9Q^4x`2INfKY$ZI!fsSM?#Ug&-(dWF76(s6IR~Ss^Z0|| z^v*NoyO=8>Hbzm#^bm+=$~kI5WzCYZQeT`EQ`wrYL0wh4f)-d2;H&a8sJPsC809vO zVe1*Pc#mc}}$cdMtVk0T?3wz3g7)++zDVQt`liy!lwH)iY zV2efKr7F0ycx6F(@m)kwiuGYweJ=e@F}+_7Lo~2PLpUC&MIx0*q_DggFUGU@C}%VQ zOhZU!a3sbQ{E2nnKM)8WgI^X0 zze>X8^kqBGGJZoG`fo|NT&|^Y@Cpe(Uy{Sq5>8`~kWZ(C)0|r1Z%H`KQ3NjAotE3l za1taOU1VDVmq@rwZ{b5L1=Ac;q(5B3W%`RGe2he&Dd94`I}Uzp9K0e9ULOblo`lQ% z{4@^!>p1u$5-!V)`T{wU4^6d%d{#^KLiTC|K1Y(X%+H}X>1QA=IqFWlOR@WG_8Y@9 z`2B1XF79hhxERMbn{Y9XH=A%Vo<>agVqTu=Wra?8u`_CGAio{GA8`i2zDx&-HjdE!mb(Nu4H)ixo6?stgua^qseU8NXE`5KvW8|@Y`f=CT@YwgTac3L4$EAmHVm4Ht^plnM z+#24Upug|d_ggwWIj_`&_4oBw+<8KO+Khts3~kc!dEmV+{SBn2zpCSC+DMnP*{zeY zN1L-x4ev?ti?Jf-i|u|C<+gD)k!ur9P$)epG!J*rjF_h_Lf*n$tZ)IX-=keXY6C3JtKD^DFSpHM3EVY0 z%5Ri@YMU!cADn~ZbZ_9C+njIFskXfN#xb|N1$t+2PC6Y!%e#d_7qQS=dFX9)MbN^? zHfT64bob~-lfft1FQwNJ^VrjOdmihUSW7k5cJ)quFNzWFOE|Vy+Z1@y8ngw%Z(5&^ zG!D@M?RarBz2-H(Zp!LE%5QeG)b@HW){c3AnI4U`)lw=`Jd+aIQM01PA)_o zEdGVkI4P%Xrlq-yS$0G(;?o+0?9B1$-Yh$!&*lZq;1OtEL^3d!Ki-;?IQyegVv;lG zF9i+o65ZEda~iB9B%jkIMCG9O?s9pqOh<5k{57i6*PJNIVEfh?z-cW(IH8*^=lEjI zD$_NKe(Q(FcH z8ODXxwJfD<6Q0ZOmzi+EZ=(qp{1#T1f33OW)U2%Rue(p4&Cf3q3%QIB;XbL}}e1V!IGPtrTF$yv{+)a`3=#V)23Zyq)4ss6V?KOF1fuEZbo$R^hOdqNQz~(u@)TVpuH#@7%0M235U&N zb7!7MqN=yl70U5KIZ}2RK;rypHd0w zeTCpO+O)e+p)KVfHv1E`hNJ#c{MdikLOZQ<>zivb=p*G?;Ik1mdtu4^^l~@V@AJRL z%8U~YG&ijoCt?`&Q}Yw!+QZMp)pOe!{$FjU4&BuA`+@b%=6cQpRBglq&S1dEIr)s; zN39*k&B^gxGHPC@x`g2PHZz|mAycEh#>)R?^~0Z?jYtyTEk+*CY)@vJVyIN18_ZTZ#)Gs&}X&RF9L~*4}FJH z&Tu>XRcx!%@dG^1B`TrmN2Xa;Tv>Nwxc~CH6Qli?;4no=lTkK^m>a2A7pjZY+tmWm zpV4oP-01m%5##glKAQta8WQ_Q+w1Q~&Y{&7yi}t3jIWw|d|CKXH@?XIDoH9Pq&C}V zMYT+DG#sgI(1Ktr#P%HQTN7M4eg4Zd+C8OuC!;ChWG$zq<{fd|qx&To12Ni}7RZ6v za+R9ryt)(e>wX+TxT;7IC2s0YL2tpm9tlUrghq|q`&sC2aA&Ya-&=26Gr_q#_H0Q{ zS?LbFV{cy|oS<#W`8y2Hlf2HgsEz4KYrF8tb+yfwq;#uBx+Ex#?5sy{r zj%oAk4MDI0TPUIV2@-CzH&g=;-DpLS+rD)b(Wf}Nmoc8}jPjl?dTQhJF@b1Zl*ITY zRn|ovJ%YAHN0M;N#_zy6o0q63>}Prq4Q?EoA8%^96QIgHif2J0!J*=IC}m}^+Nnbh(GAtPl-P$ zq=k+y56*btv zGYt50!QUUFDa!z?_R)Y_v7Am@`%5iEPrvjL_d_g8x<~J%d%e{_f?goB4B-i_o~`Q5 z`l}pY!;|?wipr2GkEq%Y?wsiJ*Q+2!X@P_@MKtJm_$xbe?~V=>!Bj z{D2}amY6W$zA2O_FV${S?KKC3;$W|AspWch7;6UFb=&1CyLjui3 zO0nF3WP9z_H;o7u|R_y1xYHdc=4m z>sySbT-c)3)uwwO*-T^5iyTCQZ)o;k#Kwey4GLw%@EV;DP%NB6qMUysc-v$+zw>iL1Td@dz#?EDioGxRN9PX67^enVmx0U3nyFSx@?RLCk2Xjjg7$*jHX{F!;3jQ*HLT425C_gs}$U|GYGwBA?6ty-$o6h zJ1uV5ZQuAZ`R6){8ur;YJ`KMcvyzQ1Mis*j!p3dzBF)+Kx@M)D^v(uWZY}r-6A2C@ zdI4<)Q)nD6yt0`~hpBLC2z5ffY}Hjtmcuxr6eZXaBY1*w91WqGWH;zhd(Z=~NVO(hc=>v5 z{jsww7V8;jrX!r(7rl2VpN$~;EEYpiF5*|Z5bovov_~)A9U#1j<7}qI&q2|e%_@$+ zK-Zb;tpS@COF)>Xz)|=E%%>>7qALr(aX|PJJUpMSu{EC=sToH6*LXNd+?+m@;Uo{A zM_C#+rs#!9*nyHpEzq{LI~%nG6PAY zfY0UOpAyl`4c7IxS%Jh5&ST{oeEE6!>nwb|Es%I~YGo*40}U$_|9&3+9V2{zTpYk2 z;oH%quX0(GZU_o&Hldrdzzi5e2vsj z&x_WslJRF=-T12UE_~!ERCL!2Su7i&-qc=rirHk{w-xPs(m0b>Q29sY02^MZP zk59?bBZ7!Of&$?*{F$;~`1Z5dMKPYb3_tyvX>T3`Bf1xPeDcfA96#RLn}}a1M~2@V zpYF}FqbRkMFx+BZu_^EqOXl*&TYKA23K8yZQ--EngJ_UnqWk)5Z{}8LBs|z!8kIwS zACq?z_U&E5#l0vm=_A5LwTPUq1Ws#si}|t~({)C+=o2Cp4n*<64@1WPR*2vtmW~Rq zytXICE^1Hg{17L-h+hkEHTNY7s=*)8v#|qyHm2bk;CLsl7osG_hj4gLo? zPS4Wheq~&gM{rH!e);k^ZYZ5P-uT$q2JC744MzWB9Q;+Dj#vXE{>agi*kI{=90xxE zoXL$>EE_9<(R;CkH}k4wVjAZ zcUzov?udgcY*EywRF^I-C@rm?QczJ^#BOkfm9yfK(pqI{K~Z^ue`RHHHKQ(|V_r3W zUxB}LxpC%;(kLh^E???jW>AzD*ZK=8mfR8N5E`SezO%HF(XhJ}#Z}d%73D^BFoVNi z_frrfdgnorT&$%6e^9=bVkg9 zUrHI|5E|<7QT&mkF@|)9IKS}^0^ta!_9Ac<^+?W4KZoPgT}bB&`hY|)r}JwQJ-<~Z z^ZrKZlZqDU{7k~-b5LV>e=zeiS;Ehk(wQyc7fARz375~IJrDleah z`f(C2^Z64Am(zJh!sT|-CE+rCUmQGzk8{Ki&EZA+865|oBH^PY`dkT@;V;l2ERvk4dTpk@;;<~v;`T#WCSYA`4G z7xQnLCy*1kn2+3I!o_@~){OIhwaJ8wdB=7WF6JFei_6*YE6(J7*?GJvnO}cTwTag~ zl`ofm@Ou_cK?eHQdi1^ajrRjN){gJdD*m}DTa-3FZ~SNgOK<9S5{Vidh9&sMlq9U1 ztGcCU2(6i`L5s`MtZm9|oR$>s$M=ULmX$pWgbhZE8p^X6d#EKyyuZc+GZ(q_{lPO- z?Bluh=heUoqN&q-dmC-Tv0sBYH9KLXqUZNwPZqBr(>^ECAC5pKLfRWu*=~XQI5GC< zJ&Z?^Za1wIn)ev7+=`6{e7BkHJ+O@jyz|WV9{6^HsvBPpR`s21Ra*_5thC?vGb9*v z{BYcP)j+J8;zL~;TW5M!$&DY*$vnGY|E!rk{ ztf74D*T6F`ly7AoTPWYAZE`hcAt;d{=Y;Z;0_{TxbXfJ(c#v}vH)wcreF zQcO<{&Pem>&+60D9wXh!$^mf)t#}QVB#ezNs%LCi4aYkFtJrHU#1doh8jtSe&W57ydMdbe8` z&O;%ygX3v8#fC7OsvpCOut)#<_|LJBe*mM`G@F0Ced~UY@2KW`aXM-N?_FmZTud_x zf@Tm}Xg#Uwik-TFq zb%fNi%ALHSCWYHlZhy*Qo(psA+Zh~&~w zx$sG}v(V+JXFT9&BpG1)f(9Gf&Y;C>+RoU_uEw&w7mMQ$;~v`?Bw*vr1!I9`0Fh>V zG!Q#K8+#s#?eTCHNt;GCvv7O;cc626g2o;X_RjkTm=l1~!3+7!oB;MbYse-e%n96; z8$5fK4=jJ^3FSvSp05SlwV6<_tncaW7edn$spAD&l5}?`sfDBjG7#YjX6olaQbyaW zTaBfp3iKJYT9U!|6X-YCwe5p_0rqi=X+|T3r3I@scrG|c<9J1f;_-xRHXWO23~QWi z3s@7q`dh)87hqi=(t-*w3W#rN0$nPB5ou#S-_qw%bT~z-> zg#k+nq=60F3f|D_XPCK#b%$xxhb0SaTXNPy*^nppfP=ZYp?WqNYnvqXBBWW!O)se zg@QjEE9dMrPEYj&SEDE&vHWJB3cVEdBWB282kF#^6U|NYt$xPn)07990%#yDz)=mR0iTb=}^dIIT5Qh)+1{Zn6=G+>GsgA>8{X{v_Q+b+9o6m z4k#bAWaAh^WHV~Pkcvw`h{_KAd^RGI#3ScYoBtc-9((<=> z#Retv_k>m?;x_PJB3G6$S$c?hyRU*aAt8Q&OR&(Hv-mD^^m%7xPR(@UA9J^!ch*(e z(rxBhNkhdJ8s6VYuv!$0j3p*2nPW4DCMD8l+Xab=jTRK(_~i@t+Fme7*)KhBW%2ty zy3Smm7)bh7Y9{SAZLl#L0dslyr*vg|U>>p&0J@v#wS5-q!0#&NQAV|0b7%@(4GCdcx9hSRb4##z2IxNU;@8*TSl+2I;W?-NcJX0y@M zwDk#O!yuH%Z5Z6Nf$I35J)d1g)&ovIr2U_^S2dPD6LV3Vwhl^LDmD=)U6;{|k8tAM zAR55_Uwdx?9#whvjZcyZ1cXdf)Yxhrag;;>Gk};_tTT{-GnimxlU6FkkO-9}B{LeT zLU0nyIE==Yw)Rsi?d#iOYropJ$fjc05^Q~2#0`tpxKtD4LR}Cy_gw z|LgjH-*a6v=bYcUpM5>|{oMD%YRMxAS12#y90lTv1*A~OU2XB|7xuWEWL00(zaIfJ zepa#RnpFHtO%V59+q09b>X7nZ$iNM!^RJ;Y+}QKPcZZa$Z=E7!`LVfa|GNAvV74fP zj{^1mrFr}U%s@-oLMHzslz;6{U+uIMDcS3ewAz2Q_NcCQ|9rNWJxTYF!2@Thz{!K- zENRa_i#b#Lg;dCeyTlfu?NJhC_$S?0DXU_B%=20F*vbAxybD)4F?QlQ6hDEh42Urk zSP6cVVb$PDM}1v7wo5^;3RjGsz%EgElPWYZRs#Eu!mGrDjB-}-Tm_ur(;uCBEo=o7 z*JHSjw*@rBwbOO;Lxp?Q3w>RSXVM37rPJZ?{;QO={Q=<@FcO;sOClrw7YF<)2mD0` zyxRdk;DCSPfDc6dWJ>=K;6mo5c)eooLKr>=`jHNJsRLf_fWwY}Vk$u{1+fetm2`=> zkO1+L5)w)23m!*~h1FA6^}O#z8zOVAT@aZM8rTOQ@q&hiXzfyQ3)v3EGoxJU1||c( zKXPnInO}>&@ZcYXh?i0phKCZriy|LIcYYa7EzE(91o_%OdbER-3t}pyT1czvr5wpH ztWX08f+$L`kgCZ1B@GcFi^Bc9C5x&TEEZ2NXi(cIY%Vl3ESw_*Ts2E;X%_)T4;D$I z2cLTssEhSW$GO3j-TcA8O&;lqs-N@1F`2Sqx>6g?{!BN?hF>jV*EAcxNa3cAKj$7S zjfFFLwx%p&yz2N&zbVV;GH{M{I-D{!3G!m{>bI()ggECiO>gQ%oAJJ*=yf?<3_o=J zRDaO89?z0Cyh@V0_StY9?<+POD|X?s`%8~H{8=g-EnCs}Y=xg>qrXDom)mfip2ap? zhu>(!b@=*RK^?CSf4!n-d7847|2A;5JYTbg&$^-e*oONg?CPf`XdN%tXLQHf@G%m0 z&9vd;6kcP)%N4%JhFc16wBeH#{;|TDUoU>T9tW%S8S&E%ywJdh8n_qtbj%lj+Lfvs zjd!VMT|X&Y_1h*py$Wod4cGJY9viOb=R-DJ%Lo=Osh(H6RCD%WOLWxe^G1&ub6yFo zhw_uq_#ZoFjMrQ0KmT91D@vh_ashD-z|Khlxq`IJ7&!@C!fxXD%)PvaHg~m zvZ!vsjRJ=Q?@}9K$H6k=Lg+$~mGTk{lYR|jLwdJ-d>^;zgXsZ%bMDoLZh+XeAK|Y3 z>(#D(qkNQ^%YsDz)YkudIU_`-D18tMobo(Bxhx_@P$S>oc!q_I!+8*C#J34vj(%xu z#NQj=gK{dkT$iT`4SqIPqd7``C=0arr+w=yReoYWx#k@UMzSbP7U0S^_8|_dm;=10qTU;OVQEUbdDT6@@(eaxua78vNWiMIu zE$yfUPxE=`R95mrCKdWD;#DPBj8+mml^GbVIhIppp`&*+WH`ymk8XR>-I`Ae+y@YuUc@wlM167YtT zjoxvCqxlGz>V44l8#g%e{3lMf1! z1Ccu%%Y)>`pBE237ols2;lLOV*uF%^q<$Wnm(X2+I*g&N5PM^mg^pSP!O5VI|CKm8 zKn41r(uk^vzilOk7or?xd9s~!M9xrc#7c}ijh^uy(im0bBcPr@&Cbhk*M?(fQHnkiIs}s3o3yaTA$(|cAoBcUuX!| z<&~~`5udDjv{0C*R&z*8J58Z*j9PwIB??b9RAHUDBHr=tuOeTE02Ffx#*&Kok?ubo z5=FWg`9gUFli!EJVP_D1A$j*jwzBNz?PU&3fB z-EC+hs2m?ca+QZJ9uZz#x8wqEFf?t1e^lv+3F9XWx4|&VBVlTbkC={~g%`NS3=d72 zF!kG2(?jL15p?52lfqNL{7Kz=CxoU>4^IqF2u=?PRK5#MDfeP;owsUgcqZO>FAja% z_Vy@$sMeAjv%j02<;q^U(zUPakrSSO{rBBZeR|Bgb$@(j;&*?2*1|y#Jo`|`q4vf- z2WQqEDz@^kJLXqsF7Li>*^<(KJh1V-t~+m=`(D3y1K;@Ywv+EYchZCh?)b)Y2Y+)) zc-iSifBsFl;n@MX71{4pR4zQF>DNPUEC1xUKfd_d%a@dIS-m4t)?KmZ$qHZf`oND@ zw-4NyUFL83k>Hy%A#3_C{m0hdE&JvNqle#;d+2Ld@9Dnn{S*5?d*kfcPYwOW&&Ox4 zNc{KK_gbbN9RAdRy~E!dez1Q_(}&OPdF-O0{uAB}zw^LL3x4Kyhfm*s_b;Y@=c8Ai z{jh)Tk^fbDPwzXE{xY+3Y1o?GJ>;2N&wMOg-0yqSuRH0vpFcV6!23UMIrA627xtXH z`ObqcHm%GKy57oe>}+gt|Mao34?lhEOPyuUj=6N{lAAsM-8yRY9i6|8JHN}75jG#MwSN3Z!{A|VEl2@PJ)cowAVRSpV3pJ1=d{?_4w{UiGM_0onN5 z^H+TBzEe+qW^?UXuJ@mOrKow;xvo2duBk6hx~+X`?iA0tga35Lh4J7cZ{7WiSMR!~ zH?-pAKi_lpz?YWaf79&1A1*!iRJ5@#;*93)+=xY2@TU65(M?@-6Ly8qr1~N$`fQpZ z>h)*oNev|p^qY8Q3hDLt*%F2G;=`|kH*lPEAk%?8XDkm^LkTEADGNcRCnRsho^z>B zB}_UCl;NyV?=MAsGMwuCuH0L*SLgPX)jXu=#NKVlbP(UFew6*pO~=RZUQu)>@jFYT z>yo3=^_9qKMuKJJAL)0ETxyDLaQ;#CNQihw!k7k*I8rYqZ7Cip{7KO)U6LaEpE4;e zO3RCX7MEga2#kLLb+=J{E4opYB5GqgC-@$d6(?c`6|F+EMm%$QG~iCU{}` zo>%e~7^Z4=goOK|{{5h4x<*NFsekQXwDA-wY57P$bOHQ$&n9fTwJM|hm@=K$e87Dr ze)Mlozn0zTa?;+o<~E&$2e%2R`}%Ll8@{Jw`Ljq>vQo0Q?*hw|{~{&(S%~+h)Bbh- zzmJ!h(!W;uFEu^m-kyGjpDF#DRQlVLf2N<3bREAgmpCxSZ!2ckJ{7-~VSq$9?O#7P z<0e!578zr3>*Ph0-yqy;|2lr!J=SHRNX4a##Blcc#7=uBXzUpMeX9KP-gI_amW#dZ zkY4-0&-9FYyMI30%Z^^^mF#S{DjYtNCuq&oV|yvtMVDQE@ymWp!eX1NJS22UNLgeI)WKvqTHW z4V;emQrr{w5`@z@?=#tkl6dC9(^(IVrE#>&5f+v+;iiO6zMm;BYOpuFT&{4{98z!Q zDtu%LkvFvpFSFs_Q+UXR^D#$gR`h+r`lJj@yP@jldR*;e5MTY!0spB3{%Z&Pe>>nC z9q>*EoD2Y_e=V+b|0ys3vi{qr@RupSTK`S!!Nyeh$D_zH(VyXfpY4D<>BU{-KyNwV z)Q`%P9vbe+gfDQwmpR~Z;G&$>0!XYKQ5HXOpugJzcPXZOjceW#p&d7GF~pi<$GYa# z)>TIrMxc`??6p&b$rq)r`r1V)GwU#pK8Md#y}PpZss)Q1T(yhmHMmArOeoP*uaEaNOBO@#Fe0?|#PgLksiM?r7Kx>DVBLa+LhK6pt1vb%8@d)> za2PgakYUviIIo?8pOzI(Q#k24r)&HL1Lqi{@z)Lf3h1=gZ#L*h7`QNYfoI0cIZT(s z-3I-+2L3yRGu!;O*7Oe;^k%v3cA$U4pf~gTih-Nu^PYj5A&uP z7a6!&Zp#(U{BoSu`PKTCd~N2})VCx(`ySo>M))TEUIRD#OJ1(@$Jb`Q&NT3$c&F2I zz5_l*;Y|Mt2K`Kf-b_!OL2vHWSnNRmpn;p|JXg&_OuyOwUodbpzux{Jgd@F~uaO4s z$2*U$ zwLCmjDS-EM9EMC>&-r#-5G#6fb!vt~+^Y!f;c%Tz=N13yco(VXA_HeQmU=ERaK@ET z&t7$9IrFDosk+g)_FrYg*Q)168?M)Dx7+Y876@Fs_?K}qjsPnHu3KJ2T8OVz?P*DU z?P5%ssm%hPw^yKS)M#m1)jN0KicrPG>?{>A!py?gP5Mbih|9Y4tt%kz1JHFx0>13N z>7%wx!^prQ9vCxn@sddG$VpRXjA)2d!-DO<$FbcrU#iGbfd*1{eQ#8er@+&3NB?Tb zP2qvmUEdqK|6zE|w)JKGb6n0;F9G0mpU0!1oH&#J?+V{X|JiBzO>Iv*eQEz8TM%~t zd}f=qyKV*Q_Qf*dFvcsL{#NE%E>ryGzI5S1{n_I;-1>|AqVY=r5t)>ot-b5a|nG@9rRM+<0Rj9<4YG8p33<1=(bg zm304@B-m$ik$6`DAv%PffeMK4EePYS;_cjg@mIV&@{*^x@t>aJ`WHRL^B?yV&)w!J zp7jS$an&z8#nx?};=oE$c8OqK>U0Z(n6NMC=2$8>dn6%s=Uqypb zU%?@$?ZNgh3>%9V6o@w-XNq?r*Du7`tceTh4?7CfZlvD*efGpm6TTsz#|DbXKr6nF zyK2heLHg64Uez+;uZn)clIX&DUJQX=HUuv}e7r-?Y#8cV5(QM}m7j6UN?*tIDBWkt zhVCZ8r2HS-8m*f4z#oQ4unJ`qln*{8s@} zQ7KA!X;KL~wGVnUZM}p4yX+ZK;n?GHn$NZWrq78#Q`yl=m+BC+R65A-7=)ia|4#FH zscQdXtYU!5+0(Ca`;~P6`h0Hh1v1o3^E0d0lp*MR@q3QAe%4op2Lig07{&UJaR-`e{Y{Y-Mer#Rr3IpFM9CeK^UEx>9W=qVE*{Q+F* zm~OfVewosF9dOcnRfEvj>L2ssKs)2K?)#-JplAC>w(;79%Ee*62& zNCx-+zWoELOzjulRyBS)Zdl*;St6hbY*)K~u7%iczO4V@@XPq^E}($VTPytk{oCK{ zpDdD0Wu=O$&xLgV`dT6L0_)%M?a$7P@BYZbhRkOrIq3e3D)E;>1?XSu)BB3kMG+9U zO7|ymYkfIBds`ljy8Z1t+G2-uul+zNmc$QOHSQa&tUX7%>hyQ8l?WE#OBvtELF)D3 znkBxQao@yXffrGS$9VSIo(uatCvR#*{2*@@sq$=?G|;qrgy#W$o*$W%Z$8J4WKkc* z75Un7U+hSB@E8}p?%bR0{0;} z8&QIYXY26XQgNgXjt7|p%?oVoFZXO*8$kQ@Y`iG|s*0Rnm*P?fcFA!(i^Z3V|LMMd zIJ2}oIj$gaQZRO;A6Pj4LAUJ;_rFgJmx) zzsO21mRD#$UvpC9(^HM-Og!54q_5nh05i>%lQW z$Cd57;iN5};2n7}@jb>VFKzz42XRzK!6vRvBF9_Y*Ph6_m-A@k>zla15l|g0gt4X7 zW!=tP_inV}e-*LDxA#8Vr@n&IE0R~unh}4WN>_Tfzm>SW&}!Kq&F?L;5_ecADLh3^ zDZ9F`Et)!yJAB1#P@&~ih_P|(bJ%ri65Wmo;K_ZYp zdA1jF#dh(25C?pr9%}r1{f}C)ae1!DpqM;bDE5&%`X(&x-~cD6bwGC^Ib@Hjw&usIf-9*lqDKQd#QI{PXI#Obf?9gwiGY zf|WQ4hexg0i(G7EF5nCFEAR?!*^Hi1}ho35dEXBGn5ZwVo=PR1>0by2tq^|gl^A(5F(LU zPhlu66}lqd3{yi4bXB4OiTiaNWy0W316Rs<_G6{kt#^t4OZ*1@H=*SwqN?M?-}M zm=*s>XobbMRmKkhmFC>68VtBR>!~oDM0cm6BF>Ky*~3795Sx7*W1-xIqsYSXb}R1o z#kLh7w&8d`4jzZgvWOii@Z9npkXngTd{l+(LJG>`pNe^lq?Pe5n&o5tA}DC(6~x|@ zRToA%hU5Em9Q{~;&~}=b*C&S9rv)oE5}|IS7kOazM2@I28BmRYCoZYn$YB*eKm{Kn zh7V%fh9K>wDr6}`z$7@tb4xE1mXwOCA+(VA-|O0p=(@KGW;j#6Ye+Emh73K70VPar zER+q>9TLe7CPD?l_{4&gZzM>^^vRGFzwz(Y>lteB@$9Nm?qkKw)`}~}=wR71D_()J zEbG^p9#i3c@yw%$=nHulWq@?XI%LXu_|UD?xa<9ip{u#_jaGJ>O^d17u%_X8np9v?L*k{7YR$kbQ26Gu`p&mI|cV48Db{?U*Ep)ik>f2&st80sOyT@f+``jbYPm^6< zX~@1MMjmxqT6|ycLu&kf)no_7DGeYAExT~ zj<(+2631DQ|Db_Gj#%+$aP+$s|1SE;qVDAQle7^;WUeF_f4?&R7Z^CiLFmaJ3^wc> zvTwlt>dOa$u}`xi*Wy1Xj6U6tNO2M-Ws!R z1QBl*$EiiW52L5uz(KFnpOeE;lj~*CrLY?ch&6#u5+_4GtTzfUE?aSQWAeL1=ZbGD ze$iUl$(XvYqm{#zJ6S7oAd=r9>A+RQDqxGs;4*YErenxRh_RfL5bg?iZYYLeTOCFwoOHAA==K|6=^~#tylo zZ{Q44w8et*1kUeE+$qX282blozV7!lPX{dqP}RtS1D~E2PFDb1nOE148zWzJ$qUS>0JraBh)R{yfK0hJy~& zDN0K`P|ZE-3B&tDo}@W5tGV6N!cEg6Msis3-B2XFA;of>@HF2j@EIsu@^{!0td-l@ zvJ@e5)G0`C&Lz5W7w$RKyxsjM^v0o~UerUmE$U-X`G9Uu3ze=hKJ|QrwH&T28je$* z=5z1@Y56*yp|kCh^j6|CQV{*Vl}N{~2FSO&Et!B4jAv{HhmZ_o&Xf5= zj!Sw5h&YUg>&mT2j_KEPJM2rR+pA~8@*F{m6c>cIV0*I{ zW}~XZ=&HMsZhRr*BvhD)E~rS{M1Yg)DT|SxTR99>V4YR~q;N4#V6l=*F@fGBh9`89 zQ!~wq@F#{l%!?oux5=d=ROjUsd|@VEwz>4D@1q4^$zmmLXOen2x2pL}te@3GqH&42D4F>fNCU3e?kwqTaZOi$HCRXWk(@xCW-A}UZceZd=r|V&(Vff=t^fZ&-HSQ!e zPPCkXaAfL5|81tFFEK`2i68UB2F8rTG&7%_&Ru!@P|tAD_dpr)#$HBk(;hK9C6+fw zstRKtXMt1XJ^A1VPsd)(Zd&HXz;HT~upyWmEbhcov+*)v)&xOW$sg}QJlhW8%l1X2 zy|v{DJeIdU{3I^n__Lw-q3&FqCd#P7uvQ;`o*X961}oPpd-UC3d49Ux^TT%kqovzD z&6Iv^;s_<`q~|KpIh$Ajv0B$*oJsBW)x}#Fo2c#V)|S2U-tB7*<)vy5)>A4~zsAYH zzU(Yd%k${evW1Mx@w7aRC!7iF%T~wis!3K&tnuyJUO%npbq)hK8@_pPC*r_7)ZE@G z?>#Le5xNBv7gp@UL(%7YPD`f~MIDU2&4ZMuyN{^S3Kl=p^Cy)ac1y6}>N=YOY>O%% zqSdnPj`N}ECWjORlaoFz+q3*EEN(f)rN=+d#^|7O&xQ{?k8}iK`{r!SRng-&k@qHx zYz$J(b`WSfGMLxs%XP8C`LK_^KlvRuKu=x#t^o2AIS%J7L-D0doTDX=yE&i-D zW1&GX30IsZOJpC0kqobwzX*HmI4XNbu=oX7vI%GH6a~$xZo24@eKNJ(gY_rs0;3Hw zrkY_umP6JD`+C=;YLTsU6{76R|GNwv5y^kTS>!VwW(VdfVja?77h92kIF9eHjYE8a z)`+ivUHqratEc&EC~I;oV2vF1R}zoNe$gR@8+_Zj)@6HIXvq(ar9V0tKef@=TId;aWVo*fe4iun*KuqEj#K~WG^^&*4?d3XE8YXvd^qP} z`b;*!X899Ye-{O*Q^bVyTnDczFHJbIT`Zk+`@n~qwpAp~Ygfr=`WEQ!7lV&#M{1Di zxdWhRzCClr*wBn3;etV*_>9%AnpZKyhU11A9DEwO{AIcD=f;r! z`&oF^bOQ!$S^UW>a9YZ?BP`3WwZ z^N^T+PJwlMuyuMaJ}0N2iiJevRCzjhdHltk3iy$61aW437|ck_Ztk)@D}Is;eu%^8 zalCjZ&FOap(FwM_g-~zk7i^7Y!@^CdHI$1Uk+YS108qA~fBN@k`e5he3PVoH-kRo* zv-uzKAKZj#)U`!OIDXM*e3WkY#J8Cxlc+$z|obyJUwPwM!QMFJ!cle_(Xi zhi)fY*jmWaNHkW7Hs9Nko^QbKy@?GC9NP}pkPb{*9T*)u0y%>Y_AiJ8^~XuF%B`6o z2XpeGIO8S8Q0roimMeM=UTrQwy8U}E7ZoomQ>`E6*G=!kdVQLhfY$a$3QDYGn^>zS zn&gswN)fn4MPS2YVEueHeecb398I$BkbD%A;1?q!EGhAwAsB!n%dum!ap(-vDQxj| z&-^FaP-lz`4!UxDP?M#^m7iDyQ;5_oUiB^iy4WLP!eJz`+{zREH?zrIdt!Wdx|O2= z6{_!U$Rn&|qg#BeTPr*Gf%opOHnS*5mz_5MWH1ZJ0Q@$JzU^fRA&A7^(-*DvIckGKMue`W^b zPp6nS@6GVPIW7JK3M!AsIiVOhK0mecW0GR6h{Ga=c&7Y$E47pKsE1|y(Bd5}`&T@P z+`-b_0b@2X;)9L97mWBV2%Qm&mAou3aSb>Ma$ZEcKe?U3e6Z(C_NH`sQ|u>d>|jno zbf~tki1y`1QbglY^_k^whs>c3d6%Xwb^DT^Di~5}yH1a_uz{HVcEPwCd&7d9q6p3A z(_s9WlvPJ7{$X?qKlYGb!HNGsL`LgiyCSvZTxFmUsb z_t;KL)qiYRsVjL!mS}f4{0a+i&(D$dZ3WHSqc6oi&JI3&00j{FW$#V0J=g57ta%N* zc8sR4aS+F$@#jbejbKdDlH#dYh)XMDO(AzydHf?V0frQB=OOj8(EF_#3>a_Yi)*Y% zJ<{G21g!ca%BP#-320LCM+jFjg8mF8?3dwMiQlq&IrMGMOM6xlxdIGN!|x!MkOLey1iP72kSwoF_tD;i2NWG-LTI%Vjg;B z^so2~FV>W4Tc$c$A=HjYanD?t`DP><{Q!bZE-aT$>BH^MoNW`6~0KJMZ#D-gsh-4X;@y?MTy=s zit2_U#(0K)Dh+prikL#8gC*J-DkVamp9GRO%o0lg?MXIA@x9yGmO;-RIz(Xx;f|G~QQc&#Nc$VDS3 zglpcD-nYG~cq;Kwm6cI$mL6Z<_P;4Y=zW@b&E(EKFPEkg&^kwsf`zA=g$tl(2Gxqi z3mWFv;>6wBrO-rAKW7Reg`HbaFu#B+0S9-Ux1ja2-seKNDK<(v_;(jz2<2Sp;B?fV zrRTBC@JsqJ>N(GbSEy$!`BIMb^VE|a*Logye2t6n~f7*3@_md>R7`(fmb#Y1nR305e74-lA?-^OSd z-#mr?iTLEzIjdl%tt6J4B<#blS-s!O_u||goTpR1y5HPeb8d0-SRKH_@w-paoy05B zm1Oy>&Owc?a+l{9P0Bxz9n)=z`FKvzFXQ4J^r-z*{if@+Jiioq$ySEZnO^eD=q8K5 z)uNufBDFHTW7PW^H;GsXm*>~h>>Mr2(GMKs*LDT=sr066lQqj$##BsX{9QPXNcdxU ztWn{QW&uuSz*po47&gu72`X8x4=K7giHUkgC8_sAaD7g_pGi!$L*nl#ob!inFUBSMUFe?^SrrXSu?o#6^3t@(bBy(Q-w*EoV+As?61?aG=zt!ufaWqfr4ZWhqct4E$osX@;>tWj?TW zJ}kiSDD;svU!exH7iGd~uxp$IfYCiPR9^MJeG2+IUZ`r&B;wjWPChmclGkH#VLW=w zFO;yOjlfTA@hbA8;^KJ1FjZI3WUepje=K}3U22Fq%KzypMBdo*>IZDcNLd<|^lkDY zY>%-S+VfAFVT^y2qSKdC&yaU#X+T{`OQ)|>)xACcmh!L5S$M*qj$c1>GexHK?^fxz z6d~)6ezkuc?)QK(E%x~5s`#7m&U9Y$0r$1|Ws1K*4BD=gJ`dv`h@Uwz(Td;N8( z_)GOb4jMY`Up_&mf}2e7ms~6<)~Sp!el}(8U*~@nFkKhYhx{)rme*5F5chW4TQgwv za-Q{~mmO(uA1Qsz9U!mA;X>Yz`XTrU}MLDQ&*q-Q$9O(THc&P(EUWI=^jich53gLg-fxgB8U*v%E+^$UdiUVi9 z=Bh3!zO@kV&m8F2I^e%{!2jTYa~A>Q#h-AK<;BI#5Sh}s*8zWCg;S-%5#MMCtH*)< zEeCuc+GVDAPjSFU02l3nlO3G+=EJqjf&L-~e4+zhsls1-vIyQKz727Eg~GeOCUNmC zh}#7U-@^$APJ9F6x>VsMrwG{fpuD)66n;SA;u{d}ZdZ7fngISw(ci1^0%em~eEZ?u z9~IuFaPe&id5<9}JxJ;`M?EPJ9#M zdZNZp7qCk*^f3N8gfrR!4?Eyf9q_rpnVyoNGTznl;#%lHf1Lw<3-DuM2S~3c@hIF~ z4)nisz#n$NT|#M)O60hi1AZp}&Ri>hI4bHP=$ z4H0q3BQ~wgxe_YqSG#H!)X&G^iwkgyh~kUF_NL;KO1^3O`KmcJ^YN2D8tQ9n=G0#u zaa}cMel675q00XKs0bfcPHDoE_C$GnByD^0xuLd!Nu09)4A`R}d3dD8b)qEY{AyTL zonL)54uyW-3ZX8~`bOy86D3E*1p!OPACjPa@a_bqi{EQl#+2QfIU%$a$`- z7c5*jhfga1E@9$T+j5mATcPz|Bh~#4!a31;@D6HSJPy;vJS}Ocz7hrsgn#3ivC-;k zc=3XWD}6pCL*gq@^^4}6#{)2V4yJfUar${aXYEqNfU_;B34jD*%fGN$pbop7I$;bg zxJK)R{Z<|hn#q={ZZ4_z6WsO{2Givl4)iOoHqwU_Bg^MF|QtgCD!!=6{paF(?b&S_8U4T+;s9jiF z6JensUwFcMHdtMc0;paHMAW26gTQodl^4W$Rjt`2(D`JmNE4zWWSk<~X^X=0C{=b1 z)0i*oZ7FKzswI&HtbUO@$}ea@cQMnf+m38(qB+o0>ZT^rC?A%>0`N6RUA4$sgRK?M zLuXyOM4pu@{7#)RX%5=KB2*Cz2thI=wjrC1;J&>9qlGLaeqrBNpTTOk z_sfw0HSjqGe!791_8vH->u}Cvz;F!5 zho8oOYv7#nH2%1Odky@cfe$tCe9VJ%4Bx~92JXc>9sYa+=bWtZHw@fNC+AH%UW)M3 z^qdFjcqztD<8uxC90Ol%;9dh?Z{TKnrm7DvzBcpqmIFRmt-nccrf0Z;o8inba5LV8 z4)`4gZifGB11HNvm&3~jZqk3PKZxL%&Jp-&`eFn38u&~DH`D*Tf%^^m4-I^jfuE&5 zJQ=S^f29N7>VV&8;G>Q3A2Dz<-j@vAq(5~4Ji{?PCSKuy-|K)sWZ-7J<EF{$RyV zVZ7(zr_0}K;N`OC9ie4*0bW_#Fm5 z%!u~^g)==Iw>9Tzr$Nte2wiUHtB(Rr&-BtwR5+ha`u7do%-1oD49=e4u?B9|@98Wo zMSl^lIz4wA_;>>^Jb~0Q{BPl^>AeO%!N3dEcP7(g(vL9ka)Z9Yz(WR}e-a+x?D@Lc zz$f9oPS48*ZW;J+^5aF1wV*GTz<{J1F1|Bi+IR<{Sft%rMFmSVcerBvoNuuL@z*t|MYv2=& zaW!Dzn~m|rY|pEVer@9K8|{C(5zb${c!0CFpZ*T%m(=q6A{!6&3ZQ>FV*JhH1Dske zrSMr2PR(zczCz(@no7}gET-fB2|7(*E+13>S-8`F3F>$?u8KEBqj5ET^r7eaN;@4U z>8mw}s~-P|Uuob)>Ylh6PKkk=;c!hwXAei`+a6ApBp38G>Yf)}uB12d`HF@(*U8$| zs~bCgqXX_$xGsP7c$D3~CiOz6Q`5I8T&G9lYZcz4t{T5zJ>PD_+tssvuj#csYPXHv z%M8MGiT`x_(efx!ka(xV(efh7r|2}^&KtN{HeAbv>TS4||LE{^I04mBcH8I!s)2Ud za4pX{WW(DPeW6O14qwY-t8BQIw@??HPSa~SZL1A$QtSBpZMc@7blLDGwVp0isSVe1jJY%%Z6+Dz#$v1WW%+5;C36X`R-(IlZ&T~U4EXO z%dzK=D)9>U#qC^Ls%lY^)yR5;oqj<6Ej!J>tbHEBW1i(5lL&QdLI?3NImd zPDdyg>itriJPbWP=&6MIVShWjs+E`koq(Igtx!&Z7K^YUH31u=gN4C^ZYV&v&}b?` z9@Y^$%PXT%!6Bq!;pBQ>V>wpq&@AUw1Sw3dFf5u{VOX@WBJIvWm&-TMD>Q68EyW1A zBQ!~>Q4a9$?FfbOoHKDV<#s|X0^99dLvk-R6h}@GDTII-s%t4Lzp8t#?^b5&}CpT zFgNp=+hpZ1D+!w*i2&4lLc^TYd^CMHxmuQz@{=yLqt%0CNwFS!_Cc2dHpte9I2o2G z={Wo(VALi#S*aGGv_K%h;O~aqO;mteaduk!h#Dz{)JTDDim79VI&WfAKy@K55uiFA zp&~iSP3_>bJsZvsToBER_^o)4e{b(`q8_2w?K)7aC2Xyy%AluDiH|8 z+H+yNt&9p_clt_%$NPNTXi$;3N0{}hNZjYM#PfO|CqO9VGJIVhP*|q6P0A)46lJQQ zIkFQ42TfuOK`*JgdYEMKwAsnn$G*sMo2ftrdjx~fV3$$5qs>a*>Ei??EDc!kzX{)) zeJEE8HUQ!;m^%59UAI;5G{aC{OsIwO z$rG{M<0}x?`+QtLR3z5>ynMfMCljH|%d??fgzVX{7uxh8|C?K>ftQ?~eYm`AyJzF$ zLC>09ladQZxF;l`4qG1YDcgR1jHMfnzbeWiT=Tv%X7ZAi95Y%EUsmGi0xpSMx|vHF ze>*I3(eNEhP?{5fUU>zqryzWhnlm|i3dJ`6!B|&rtR1TOp+UN|dX6B8s{g^JMNafy zs8MHSMSqELr}sB#oqcq2P!$|Rps0oV5V~4utl{`mvPxhZD|UEDF#cFw=Ok!5Uo*_V zU#fy)lvCPM?EM%5BCyjFgTY6o`z3WH`qe1vpx6g4ts^6Ji=aagbFa$r;r%$3w%bsvuF2F- zxI8~V{S9g?sCNuwp1Lm(bg_P`a<9nma^I4j%vtT`dszZ57x(EwmmxRS@7wwJ8}IK? z{u?nKLAS=eDz`G$FPQ_yqFb`b3)rdN3%)?P)Uo(Vbr-0}@IRx1;PN7GEEe^Nr*f*- z+R#fGkb=Ywr*qShb?i0)bziTTD3+=y@^%?do_b%Xu4gG+*QbJA&9-H7IGRTx#!z77zZ2Kr{$lJ@elzXe=PT#E?3o)w zjyN$z1*$GfaJ7#yeAVKBvwdfxztaIXQ{9PoTp zVW#w-?tpWTdnS6G$&v}5^0dZ*{#On-Ev94&zrz84 z#sPl~xYz@##z{5ZxM-ItQ}|*preIdp$bDf_!(45QY~aSo#Wl6qDJk|u>Wz|m%J9?j5<}GijU2A$9 zTc!b)yBi9%jH_un+(nRB0ADQ7( z%&74l1mJk#8dKv>893MW8qX4+Taun@7LA*G+syENY8MD3jxM!*2{k=++L68 zsd1I`XBpw+RygLDzZZ>i;R}uXR66wfL*qS)p6eGnjlZdIwn;jT|5M?5eAoE<3TIyE zH2$H&S$A|A|5V{#fu`{+l}?r+our zU#_#^dY!S`hL@;zDP&LFQ?7(hDcRHcIB%__1ahX-|JX4&h-s{U?7u8~I+Uj($0Z|0xEIqETDP|J|UHHZ61ajVH$5sF`;sN^3Zf(o>jBGf=p9o_boX- z3x<=E*8@mSaZA1vj0__BSbKJBfACbZt+KyFUrmnBOHLlBunn#(QIW;C>@{iV zEY%uV*=Sj!VSpDa zxZ8eD%O!$k9W@xmo{b;2kWuUv-3ubbF zW?Qw%w&aAO^43KIlNT3D1~NIrZa0Pkh!bc5ke2acT_42ShfsXr!SP%9 z$zYa$e_i~>020-62f&mavNR7qzN2TQc!QXcnHBG1N^^kd}+bB~@4RS?qzav(t% zl<$gpyxU%)yiuM;R+n`fNg+$QwG83(j1qx0qtUrs2m5>JX9(oPAL3EVmdW%V3^LZp zhgiQzF~G-QLn|>rMdCjxDXK`q%(#$C;p`YIF)NRKz$5#BtWp~LKUDuDMAMBS)KS#-Ja5~snH1_xlXxiJ}&Wk@!1IMsadcDt0`6Rl<+c19WLmxqJ zFdIJFw-oNk3|znrC%@RerOmZ1#Apf{Mm5)`IOWX zCLtk;#<2oIOn~OW#v(EkZ2I&x&rLfK3e2fmIS&gl0$y(MBFFxN#ed`ZAF)o!pzpa8 zbhMZ1-@A#c5h1GT86w0gvc5PT?Zeq-=(gyo5Gg5nk{p|GP#A#); zy?S8mm;zLstg6ouRH6Jk)wylGWT*Vwon9M$mW<&=wlLT!GGz>$fAu=+1*U3pGAEXM zY5v^R5Dwsx?Qu47IKf9$cB-1P`WmWYII?DxJ+M%$-=!2jtje828N&Z9vVw7H4Jir? ze|jyipPgg{1&Y_IdA0mDV0Gmwk9v)NmcrGir+VvhU2lt5zhFx@9oOs(^UL~|JXHCO zV1}#XpKgMI zepy*TQb(lX*B7<{(>ciswEsGj825Jnd}hD4J8dVcT{R5K3dpO`$fi#mH@Jk$U z?#a&-&H@KK>VU6sz;AZIf9!zY>45*v0dIG}A9KK8bifY*XF2atja!eSw4alye7^30 zpW}dE;D9^X&!L`grg)b);5RwoKX$-rCnr<*zX#58(EFGlLb=e8T6Vb&-9q_YS{^Kv%bcv^(^sWMmV?? zj_rrPSw?;Hg~ku_22QWvG~TG_*~jTL-cR9rj?g&mSJAOg(P>=UjnL~tjUT7z*(T{U zexky?0!`yzQ@GEDYuQ?n4IirLhud&%>$k*)pRMS*-lEg#(RL$BC7i^vuQt4?^xYvfOM_0gx>-Fti8?M(ytu|b*6F9|c z9TqR#=gAVzJO8{;_OzQ zS-S{~ohW$Dk5$BPI0U9n04EAr#XE!8GYo#Dr<=iqW3R$f2Ut4_+ zFR+sts2~-uD&p^`<87KnDzL}2=?4Q{kpc3=<6SG>3KnCZ|C=k$;8`15w6Txp$;xwO zk*GxUKt<*hC9i=#iRK^Th*!^=9jOB~aIoq(LnT`{zFxl$ zx20R4m~dppT_~Nol{|;yKUqV`YmE@L;ONUEo|bFzA5KiFYTjRwm=8vJ3Al(*Hu}9N zJ{cRB%i!RHCnqWo=P5$JB8nye1&3{T(Y>8a$(D8yc$!zjAKuPFE+dU#w+Sy$0lEf7 zNLsuGCr~8mQOrRoLvPTU}V_E3HKPDyY>w$S#KmfauKvacyLr6 z_D;K_&o+andmnsm!J&sodVbA2B!WoRIJogNSCu5&H zP9Vk!vpih}M`lEz_keRaMDlS{sBa{r6o^+NavwU7B8>#pjVu(L;fYf#5{;IAEfrPJ zr043|#gVi)Z9IC5JY6Ft!;oSTD)`8K`%*0O2$x*qtW44N!Z?r|=h*?=I-gY$e*#jl z`J{ov?L2i`kz6lenF>7cg%bUss&m5XG!E5g#Ho@DqNRP=#4RW&(RlClHNqG5E1Gbn zK1!>&;$T~GUGKYHTp#kS#gzxe;iB_X4ss@pX9shymyWzVevfz-DLXp)#+6o$%uy2r zhig5}kZ-xfxm#$ax&&KrMD9!^@@Vg#)^7n9N6Q=3TMhyAv=n7{1<6B3l>7X{$r**V zSi1n*L-(}s{6?H#hk6fKJccz7eC*KVac)oZNThyTuBZ8NlLDpq5o|4ey$1!zcDjvw7FBpv(o zag@Uq`Bjn?-{(w+g@OuF4qiog=`%|h&T93364w`#sZcO`b*>C&a{m2l(J^VlgbTby z<+WEXs9x+HGjjCE(Idtb%Nv(#WW%yWk?JdPjVzVd`TA}tH}AtjVZ=3(=L)z+UfIy_ zKX|Hu7g=KQsHHgWqzkC)Yql49&8uFEy;g4hU|jRmM{0#%o!r>$NANKqc z%M|}Ce$c}G))t}dQ4(eNC*4=cBWFI${q&k=!=yO#$aU^Xm3Eo-!SlP<-q+}(*a08y zfCnA$iyd$$9{GF+`fD8URlvzx4X6qeV=%BEI?(^p0p|)gQ#ovMz;`&{e+JI<6sX1) z$1~ki_+<+JZw~m&z!}b36^@t_fW6^Bf6xJU;(1Xf^iw`d7$ z$S%3IcIhZS)i!FpW?^mhQdn87nO`eSx-P~9J!i?4-;+-bS1+g+Ph`ngH!SwgSy;RH zs>u9428IQW0SR%{nb$WRik$8Je^}+@77DSfGr~2rX3t@$qMt}SIRm9TfG)(x8lE#O5 zqwM$ccA~KLo3==7+V?e#0jF#i8>WEMR)f(a()%blJ(#?_nc}USQ#og7UgT8^Fw(5~o+vz_XAha0$=PJScNk>2FFZ!~a|clo-3oAf;E zfR5pCA+6)}IpCKWxXC-#`dkd>L?fK*4SLS?I{g1Ia5Fvn{5E?yXR7gzIM*&ZoRJRr z`3`u%0S`OiQyuWj9q{iPxLFQAFmO|!V3mQJ`TD@XO}^j!vS(jPQ%Q?_xeT6gocDXSP};HIqNTLx~@<8*N07>+5!SZm;BIyV@& z7k+d*dELNIH}L-I`-0&QHSl5sKf}Ps7`V^CCmOifPiGtWnFjrG13$~a;|4y=z)hRP zX83cCk&ixHeC+S8~E7TS4Q*R9wcBE5rU4pM7(4aHF04{Co`l6Cm02U6)=HZAR0(aW*{h+ zz$BWsZOnRE(e)B9>!Rzf?5+!l7cvPX0Tlx(22cs0&_lRH5eOH__nzvSq{+hW@A;nZ zzpt^=b>6D3I(6#QsZ*C8m%6>MR$cLFq~20=c--?S<9c{_aXq(dnugf6pQg*_*p|4;|f1* z1}S9z;LEilJiXNbdOqKvNL=A3CxyH5p24RP|G)l*%L^dkD%^MCo`Cx`+;sVHyIDvO zhT!=M?kjMQ$NdKGU*VQ=)@co>A3eOQd zJQPm{4~M1xBIw&WW`4{Cq2ey*;0ipma1Xi5}uFXegbz_GxNA&JfFke7x$lV--4U2dARRkA>+?X9=;XN9Na~?3vhdJ{|>i^ zdn#_yn?bn4*S!d&<9;3YO<@pE5ANBx@5Y^rn=TjbY}_%pow&o-_2B@X*WtEvdZ7uY zU=8j!DPX#mn!@Jbf77){B+N7=UdhAD_;ZvgE*yLSfqdK#;;!Q0B0PVOdpU0E=96%1 zxUa!Y*JH**NaWA(V;}<0#)Acm&7g$ITs`=CWhz0Rm+>e?;9)5^F^YFCd zUd-v9k0~5@$rN!P4`-OLaL|UpeB5_&U?hLe;?J>o-iUh+?mir&ho6U^!SiL@Ww`%{ z`!~1)xasPLI~DgIh`=QokFLK@@=zR}C^5_cYve zEioQlf1|oW-GK!>cr%`N@bHZ8XyGy*?rp-%LF!KlxJj4Ia_MDFaV?9Fwp=4fmf_11 z22>xO5ScCXkGH=Z8JSr=qW_4&5rW-PzACl6*dAAYPo%X%c*TZXM#L{KA8v^pZW$yv zdV{HW`z>F^#7Ec0TZQ$jmTVHwbT8$vvHF?k)0s@rHk~5`W#)<=jfV;yBIg=I_VebWltLch}m`0 z|FCT+N0@Z30yp(Z755XkHQXz3lb(h98|mUxxT!Ca?vidjhnxCq_<8|>7db)wsD_7M z$MX#yeiP5IesV~-Y|sriW-K4GW6#q1rgt}ueDmAKX7x=eOMU0xQ9Bb}csO{s{N|qQ zvo9~*|NK4Ls0BA@qpmsi&&c2Iq&(Hq;-Uohgef035FOKyq|MHLH-q(KYSN{748-K~|8@%?~ht9wF zhdtI7@BC->-aaz7<)NuVc8=EG{_B7x=eKGPW!%_*`1AM9`PzEzzFV%%d9M1o@2(sF z`3`wjYr>#AANXMZQ;Y6-_vul!MW5|@cFUec@0@vX;ii8*^>XKTH!mMO=Js1l%J16v zq;@uA>5;uJZ<*P5UrSzW+U^ybJwMwI&2Q;_YRosK16%*_{U-yS|LdC0KYvV3^(te^cb@dI;-R&=Y{=vaNZ>^1gNLn#u(8l}`x8GSGG4biY zG!1xX+|`Y5?RjcY#)-VhM|VcwpF8l$pWcsZv3|Vg;qqBupMCVR?4?I`=RLA8f8_DU zfAiV$g>&aW^5M7Fy&pDlVO6-`87cG@Wa@OLYn9dTRZI{I1|u(yzl+$*dnN{bS;WP0 zn3;>Ff8`SSSb)dl?s;7cAigIEy4NN0ejfk^h^Q zkn^LLkaOE5@Im0`>Wj$XvPCFIdEM`3?bx?#aG-I5E*b|fhc{kK11GN0NJm#6R&HORoCZu>8KCJp$K;Is$$`S9 za=eYztFI9Mx`BUaJcMsJ-ujvWBy)T+>f0Y)OW7U+;KedumiTE;H*gnEpUKO$(?E)A zJTJEolNE zeCc|F*DIdaH|&SZhde0%)@+untG(j6oQt`fZAP^4DaUJGGk`ld-k;;~Jbfz1m-Bj+ zlVgT!rtuK23d@;0)mL+T18=WZ&S1D)(nsR6eYBB~#x1&D2hSq2^McgbOSE;aD0 zj0f}6iOfe#pSBoKNzhDSe7;;F9qW#_{|!dH!E={5H-{J53&Nbr=uf zB%Vw@T+Smn{%@YXIM)FBaoo<*#|SMuIKUt0xZLbq&%^C?7n9q6`eWa5xfOFcQ~#%H zFE2Mdak+^RYsb1||A8zC66M4DSK?C>$-lywL@Od%) z+`;7;|Co`k8SO#WAYN|m-3GnScnIwrFaMJP@a7ag;q6$%``tCX+{H*nd}gK_>F?+H z4CZ`h|J?w_b9_oTf3DA`c|ILsc{UjdNse^oGWjEaj>j6Xkj2}pmL_Jn{%kyie{wn0 z_UNZC^ZXOn842&@>6dYS5@#7eD#x#f0#Lomxjvx!?9zEY@qF9~kEeg+=`+7D(xr3# zzmoHr$qi4CXY@W#UsGeG@8tFUBTwJ@f`RklQ@D?}V`901zh*pyw~-(5*;;Arc=To!Dz~>qd;V(R&YA#PM4xx$jS@Woo z@L`@_3Afkt25<$h?`1rF_7VgCJx{-i%cq<(PI^Yy7o5-LT!X&GcnCF|&tfizB971G z`Ls71z$RYqb>Vz?zZ=T&KO-5*vsyONKgIL8gXf>ZD|9=@AL9JCmKy-?20{_fzctIi z&+znpxZG-28Nd}>{sK>5lVRXgPrAP5j{oXPQc@Y5eFlqte+hS)ehhNo|zWB|8A?sWZ|*EjQh zgPvkMgj`;))ddDXYfyAu%hRvsb|VLT)Abjge{qgMlf3A9j^|U&^9j#225`Gk{*jR` zJnk$H>jUrCVLP^;=VM=Br2pJ_2#2`b1fHH(3ybwxv*#Ag%PRHFEb(P!30e6C^9lgw zl+C2z%))|)^MtJN^Rw>Bn_W=q%PUDMoLO3$S1L&EiCH;C^XBK_wFmPSGP*RcJDf8s zCo4BEr(_{KMwS+I-aLbzRe)EE7Ygh-w|Hhr9t#%c%`C~vnVB<(k@+QgdBV(DMI}Cp z%quIV(A>PaD1?CAyyCo)xijbG&0{2`&YD}4OHmg@faSTUhYxv`WtHU3EG#U_q2OF2 zgyf|U`8fx3{e`}Q;zGt)!CY2g!8~7^YR`P z@_l)Qg#tu5GgrvZDJ&|@6a4c^{lz4Syj^N!`eq-rc5lce-^20*1Wt0T`zY5B-fdx^PE|QdGlub<_Ii2 zFR#p(RW$1XgDQQnptzfY_LmkE&Eru8v!Q4HlDsV6!eXc!Wt3CI)H^G8o&f3P%q zxp^fe!u)(xWS%ddrJ{PV5=f}viD+0PYKW|Y+>1b2rDVjyNplK{=j4@yQ_ew;fjU2! zM;6fMFYQiNG%vqkHY3BWOgZEhlol7xT$lx}iVUU^Tadrd5Js_Y4t1Waxqe?>8ETvJ zpwYfY8sWld|2&qUxTGit+_MY{@(XfE)J*K8qEsZzaIWxRL1AGQQM{%sCR}U3(f5eA zqSBeO3iFIMGST6@=s7!YUS0`Gfe9pojvUdzJ^{rPl+Hmn&n=jpR|-X=hMk%7Aeq~l zr3E=;JadIGw_s)d&9`Lb6c)e$XU&1#C=zmuKob>~DIulvOH4p%-n_0{OY;izU`rW9 z?HR5-17;QiVU=XXGAzs?e+9+NMn^K5v$Qxbr;EAngb>(*k^-*0C8+r9BA8mxj911x z2C;dJ4-z$vIV9yAUztH%*krj7Oi9tgt|TPR3saD(Wzi56b-%oPL#0adN}vSH?6CX| zxzYRiIrDsl)b*$z7v<;9TqqP4%*wg3wCKj$gsePR)tMNNXF)^Q7!3&OfD|*!e1)Zy z$|kjoj^tzb3D5T9`TKlzPJZIl;dSRaQhx3eR&UjG*4gE15e`V zt9#&gb9`kFd>O}Udf<<7d{qzpIgXd}d2HCv4%5{=@H@HOR$cF;MLj6dy{z*|iCNK^WbOW<}>x#77By%u)~Jn<5E(k1W_Cj7T1 zep(}p`eIrVo&vB~-GqnRh9cTcI266>>M-HqH4GLR*`0r4S8749nebsId<4g-`#fvH z6HRz{4oA^RCY;vX!dHq3Um6DSlubCML|s>=2@kJnQRGw;j;UnVm2JZL6pqEsG2vZv zDZ~_;aFTWSDl_3y7{s&Ogoo#p6kTP)xrt$KRh#hn3^D$!G~woUuQB1%P3c#e@X028 zwF&1_Zx*}Gg!eI}uQlP=73#X0On6KeDhSOcoNo!S*cKBWJ|965ttQ;uzjPB`!*VeG zw3~3g1i>OYO!#G{^n%sUTas}<6K*r%q6xQ~@G&Ml&V>6-c)SU}(}a&O;bwc4Xu@wW zrB5>9kD2fk6aKsjmrXcdDq*piCfsgHKh=akX~MHj_=6^VjtQ?a;l(C=nh7s6;o-Fn ziY_ zI1`?2!e^N9IVPMh*|FGS6aKO(eVGXluh~;{xe4b>VoAALVe4PpZqY1Ay;fe`wGU0zV;msyI*Mzs2@GKMFYQo2waNUHj zFyZYcyu^fenD8r2xDef~|3gf;&4fEmxZQ-`VZ!4~c$o>0H{p+(@DV0F!GtH8@ZXv6 zBopp6;VCA3i3yiY_)HU?X~M5E;ZsfcD<(YKgjbvJIVOCl2`@I`;XMb6E;Hf9ru5|| z{B;vvWx`WTc(n2*g!@c*rV0Oz37=}hlTCQG z34hUq&oSX6On9*gFEZhB;IK{KfEkv4UvKjq-meDT|9ZB?RMn6TgmFOBqKTb4FQ9{*>UQTp0(dCSO zm}qMLP%)!Rhz6rVD4Wp_5DlJ%P$r{i5lvHoPzs~(Bf2lqiHyF7=oq5o89kn8a=Alx zMyC?pk7$9>cMwf3cBuV4fNO6inq1RRE2FO?xtnc9$@(RDr}KSeZk%}@=aA19i+QmC5I%ZaA05-Ml(!$eb82o*EB zglJOfP&T63Mvo_&rZOQrqf?2#k!XR@ zcMyFO(e3A0`xBi=bStB;BbtWbP&1>iCYnrtsFu-#i6$2^w3^YE6FrLP8bnoNDDn9)BGokVmtqYn{HCLxr`=zT=f&>u=+^e&=*OLQWmw-Zf6 zL@1uoTZkS*w4Kp)MAOh75*WRnXmV{r?Ppp06P-eIE2G~gS|Ylc(XSDmN^~uwUm!Y- z=+%sVif9_rLN$zjoM<=E)r?+F^f;o+8T~NP=|mSZx`b$%=xjzmK(vSGOh(TldOXo7 zjJ}WPJBdzY^gTp-iH>LVc%n0iwlg}F=m|s%jJ|{DyNGT-!`h$dOrl#EeI3zv6Wz?{ ztBJmc=vqb(CVC>#s~LSc(UXX-VRRp&Clg)G=m?_kCAyr^XFdfzh3H~N|3vguqO%!& zh-h-PLz#@;NA!I}r!aaK(bI@dWb}5TrxP8|=q*IgAllC8I->6t9 zi7sYz3DGnFg|Zp_0MP|RXEJ&g(GL)v!sz>mevs%yM&CnpA<^-S9#8aKqV0@MC3+sw z0;BIBx`^oZ4%YrelZzN?W%PAKKSXphqpv2qgy>pE4bb#p9 zjDCvf3ZiQm{W#H;L{~FejT)4)}lVBj~Be! zweR2|s{`GL@g}s&!H!5-o7yQS?<$QKhrx=;TB=1>qOL`})ET=GNe3f{_>vzQqM{MB zOp~T>uFn^TQI&BH9R=Y#@vMnk5a!`>f9erzqvllx$zX&_6oK} z1UjtZikFd{S4)o#=usJ(x7^D3kyM+qb=iS(TuOUT@0HM?S$i)#a3!v5&*O47D5sVk zWYHZI-Kbe_TXwKJx=}f^Y#)nmrsz7&I(FHigFovi z>%bqEBf_ifbvtXx-xSvj64#8gNP*gjV5>d2FRphT=t%Y#h^=>>a<^4n zb1&|3QHVw&7T1gYjg|uqHlE5R2ZA)HxE4bg@`-_kl z%|LSWsL>v!!tp(X?@`CuD0~>)BhIVkI%K)g<Q1 zLurCAJptXSY>`w!a$29M-0VLL7QXSI8eS#FQCTZiybUp+pl`r;EAfqY!0-1TMI!uu zFZ_{}CRw{`$R9}=7j_0(BPDT-t0EGr6zNqu!gPcS8Uvdooev`xam{D~enIZ70F#j` zUbR+GLUF}zlaSP)TXFY?hq0EOjkXheTyROFcwTesnp13>?3b(zXJ#Y-aCob15O`j|6@j2Jzlb zm$K8jjnG|TRC0ok4wtjp?fg-4w!56VxCUiyBcMfG^M&L*1J)%cuqL;2mlWI^C!x=~ zd*ih&fO>C3FHjCiz0sMy&)5P-EMDbw;FnIZ@*U{1SG77=%XPJo>Uzc|-6B+OMnga2 z*8^w`iyXM#VMj~1CZkowCkybRb2GL2Dr$eIMjth{F9lagw^(DKCPYngtX8ajU}K=M zr|o`ddITv_hpbpg(PahNh1_*}pwRKNT~u-~xq$XlE|iWGpDA3IvVAYBc$4%^39vq+ zl73PjQjlHXaCx}zN?G;Y{Szio3H7dXDYeWJDjkOPoAiW@O<$|g&eYs!>u^I?U4i-m z>)qYTRf7SATbbI|T~RAmT?J72F-;pHpo!6FRWyIJY!=saHEz{43^BKC)vp;8a^OLB z=%V)HaPfb&A2P+FCtuQjpsREHv6|bDm6x&~tH^$!FQONF@dri(mSj2^5g+VSw-F(O z1@n&fjP0^eEo6AmlFWLz992{w;wo%kCYkI@HSv5=`%>L)Uv6RcWl&d>8}=n}JM0P! z3Mj9mMMWB?wL8rr6v)!mRKj)+aflUkL>V<|bvru)x&@N>9^;gX>TE!S5IhcdiM$}P{lB9fr&XS(`^;OJXdtmL37XmAuF>B#$Ch zOyrV+`b}3&t>!BmcM<@{^s~`nVT&Xm{d6UY-GpxKY;o45P;e*g46u61xf7N_Tm!=ugpO06r)I2j^{xx< zwYf0vZSe%oMJbcko%7x4RSt%%Y~TYku|nJ1-Tea`fR+4ev1%a_5F2pKTo~#~!s)MP z>s+DV88mg#7zg#kDzwBc)&Z~+-Hovy`eBy>jTRD4tE|L0#3z?S(_x`hBt!eHWIY@M z$%P7u7K|j-fq8(LNP|*QpEj9?H@JD=IY6&_qHkk*L z3#q2ZjCp%<=TcvEpw^KBa@H^7$NWe*&($T@W~ zoKIOBZIdI%*+`F% zalBZ_kSmDNV7?hJNcommIFFk6L=b~)#$SIKpBc(uF; zvVJ<=ij$SI--hUDS=pmEF<|t5eJKl!+K&kEu}6QG(Tn!m^t|vp zfMvB1R#YA0@T$2EI|`2rJ&I^UUx>fJ(ijV4j<__KbAvf1AIS2otS+_d1MrHhmfESD zbcEHk!pd5IeaUZkxRe~oBMo}j+V+OttiubKLT*hQ*ov8%3tr(6dOu#yX;4NlU$RJ22YCVwR(EpS;$BF9yWs!53+2ao zVU9<*l<%kxlvq->%UaB1PH3`_{N3U=B-Nt!r@5r~Zk=ptR?gs$tTe)5Brh^}EM9`G z@l|+0wSCU*Xoj*^QgoMcwCyd!UUZ90f^A{qBDv|efWky~ip>M#4SlXt7cDzi2|suf z#;5$i8H>2$1BA5HPDIwqD2Z%)+dy6p0IQMeS)@`&Sz!M$H=kmwJn7pXT1I)W^0nBP zSX)pPohM1kkZ}5e&d$*Dl%|^W$MZ2IAxpz1jLsvNG9FIyiW~Ax`Xh-OHYyF!B8TOq zegG@avN{!d0x_Z+LYhdrXM#5R^$A+;>sGHihYkO6GN$3;hD@6$u*u51=$0SAFS4Od zH6ox#Ad69w>OQAVJcrhk#dm9&nCq0Zb8_%dJoLpOuJ|jM!%L$wls1pD6DHa1+zw@6 zD+H{bl!K)9MGK1WHlg8UOP#wCv)fU<$7|M`B{pEmq8shkB%f-As@*In2Nzr>pV~nS z2+2Q5;&ZjKa)zxHD9Kc?v4Rl9YRNwTtCHmkWaq;?0#n;YCM!>%5u+(cD`pI%pcD11 zmTVUb$xVGlCd+~sJcX|xg z)V*r9-J@)xyzS1DUbT$1r43#i*#^w0NnM<~<(y{ym;Id>BX@V(?{qbG=$%^35U+v> zN(+Wh=SkK+YA@E6uucR?HClO>aw%JQWAi?R_C6sg+m<%7{#lN>Q_ZGpw5oYXgAX<9}nfEZIX>%W=6 zAjup%ga(g8-?R^%V38xHOFr;N9qMF(c?%n@P~t|iI6u)Jw;%^~IB=V=iuJkDu=Go2 zL2JC$nYldP@d|7mTKEyO$ebGB3^>nNyvz_tt7&*}tCPy1 z(c3R&D(qqZjJL1dt;Ps6C@7@Pn|%ko$|)HG{|DbQg+Z_P5Py`lG4l9tV^U1)DMgXB zTelp$#u{XS!RElU8JLjrRZ5H}hJWo*zQn>KdI;P}c#PuOR9k-XI9tA$F%AYu*Zda! z@{_PM%0aMXLr8PO?4;QrsG!mYSvIAz?0arvXJONKjX@qHxXUQNWHYyL%??DVKK?d5@54+QV zmJO$(zKAAR@_yLp_H$kDS^L*M|lwS`Tu`Y&j_rb!A>htfTF| z?)E|!>5#w_dxDO1utSa3<*Z%A4RCg;s6@=vM#39PI7aS9PAAPFJdxzZ#=~|ocjJk1 zFabvX;)eD}3^N{e2}T~g`~l6A6OLlSAYI8Yy9njQ+sWoz&APUHGNjKp-3gf43qPIrE6~`c;U46w%Zf>8_J(20qoMV9z7G3I=7zWrnefS=l z7|gL*&}t}duFVp9fJ!#_H$;0t-~AnvVE4SjMIHgBQkNPydf0dPLB=>qlL;Cqf5=eI zg=oRq7{95%8@->8_-A%9{Xt)BMDM4*h7Mdg7Bd7`c^G&-p&hf0n?M`uvCJYIg>L=> zW*`1hC>l}S`a<)6KHp~kmAGNPO`ne}#0@m=m%_AdB&UOyUI=3Y)noJhuJsDje4pm; zEnfW5`TV_^GH>-LC%l*%qohS_r6CDxrb~U|hI?%jR9|PDo8~@gn$?k9Cq9_~1=S~^ zmg0soo1{&5V40vnR%Ls_9<=_72S|T&>S;b=U8bdDZ+bMFmKf~Te8%6N zpK!lzygKQZMC_1Y6#(NCGPLNovI>@AkTB&u;u1Ntb+(J-i(c%uCD$*Gg5jQFgU{u~ zZs$&NH+QfVmpXAn3dLLVmHTYY&0ggw#oYmgfmwhxfC1NEgpyU(md(Mda4!+xy^?}A zupoq4Ge#ya49EyCbS@EnKPhw)tzgub-s@U>=}!MXsv`7wY47gN&T76C0^^DWo9^@% zCN;HZBV{DyH>O~jf!m^TLN36MGi_zK;jgnbKiUaBoWv$Pv=9qf!+48pOQ2N}S(+WH zxBA>V(Mh= zO+TIupEA%HzceOPOJh3L`@N1Knq)!hAxg)oquBT97`E+<@^wNZCEF{py#QxzHO6zd zr8Bu{5$5WRP^C@aM9|-ae2v+UQjhXIiaT4c+W}08^r-G^SxwUGzo8`NC3+%fWa}Rx z8;?5K$aU0pWQNY5Af9s(a_(Yb(FmLVJH)w@&n0QbJ{Vw*3ugy(M zwNRkzokyJ8UCH|wP4BWN&}?EO3T$wu*OS~R2jx2&{`w-1K!+vpZ6{vTTsH`!TZbPn zC4aM|UYp#h;F!fQOY^o4coSMIYYjunl&!4B+Vw--lFmZb7!i3{W`y%`GZMSN2PusNp9C*1+~QUz&w-P_>JmUT7pC|X;w z>4$+y$#dr{-QiIeXX~G_%0aKG(#?ot%Me7=6Tw#G+D+rM+xfk;0X%kwK0=>idQFLc zjcmx8Qw*`kv=hJ(YDPMREQIF#LN&u2bUJNmz{p~qmYG&&Y~lZ3U|(fgOvvQ(m9d;yK}YbK^M%q;C`Nb^NtoY4CN8p&RXQ1d*FXU-KnJLre^-(|^s>103z4b=i0_MzC|a*n5P_ z1SFB{Q4-m^6!LXKe}v+P?HwCUe(sh-c(7qZ3ljpF9gHZW2?_Ef%ZOU~4G{p2>1XRL#< zaKO1)kAeabi-|O>Tx2}B(7uPFQ4d3&6F;V-b4=g#M6}Y!FrQGedgP-QgK1j*W_hOA zjaBBx6lPoWdS;7SXtHje&v&;Ars-z={N)1u#IO+K@IVjWVcB3{WF_8@deMBR0h*z| zN>Nz4rLY?jUgbRbBA{+(R9N>4POluu!pS8n6Ho_>6u>23fiOJ$r|@O z^}2XOVv0_|ffPhp6z+|%7aPO>Q;c*JZ88={v4N+2hS~QQ;))=oCw)Mh4sZu>q=f#% z_SI#ZaGTwu*t)rL_Az zy3==X^m%`y1S2U{kpqqWF}&LKp1%pZ&Ym2s-PBov9oT(6YpJphSTezkVxT!23DlqD z$*H4aD6y-`8cF=sqRgdFaJ|Fq)qw?CtcG0eQYNFUX*AbPF{%@~5BAB>n{{NW5GUt+ zt&S02j~7BuH6yN?w))OtiRQ#a=WhKbC=^`UCAW6Z7t0r2g&$47vTMB1??mfW)K;Na zFuUzL3NuTi!P2kTYLObX@3nXVGdb)-MtzN-FX}k$L#kt501>+Ea};`k{?Yzr)ZY<@ zb*fmLRO^kSrOuQ3Gw7d6R6gYu^(6jc!hsD_z_i${1%(O4_V{L zB-F|`eLeLG(xXOwBw+Mx)Na{UB*^K2rCdN-wjEvq(vSzO;$tm2RPhwUghcYGCd+QN1@43z8$Y z1c({H20~*xF)K9ovRvby-I#7w{`%Q5Z0(-^ zX8lSWN|-P;3l%!t-4%_lW!Pb6sIM&rKlAWwoVnw#k8aSQe-v;k-9X5-|U;flJypHAv zUt(dy(TYJ)cfmRPS<>E%EPLaDuQANH0JfiMb`5Xt-E3*Vs~-lp{>o)(khBjY?p@oc zEA7fp6kX>U-h>pim|7eBJl1kb(w?yFE{=C79m<(g&BK3k4X;If5b+y>n?=iMNqa^( zy7i-=f|Jxur<$eV9mCsQy=yIXNYNDB(#NtzdoTK3anT8^Pblz+Q zr-Th)#Ige?kQMMJ4R0RaL?|)P%osrV1I8t;QaU6|5|N;Ncyp@u%+;1>$NfSHgN%!d zmEICAy=7&2fw^>6D)Rq^m8^Z(>)lf>)yzPu_C&%zreP{Vj4&q5rL$7;`uD74?U{&= zzn-pe28Opo0NQ&)ukK&{Af>BgJY1~s!{Nf8erV+{JqrKTinN7qb{8%wXHa+P)OJ)J zr)yaKZys)Y&%-z|T0z0mNs}~ui*#xmIKUCL?pF9PEQeXw4n2w2J6v1^Ak1yStyfxh zKm;lON~u-aN%l=l_TL9Tzru0~vcLPj`+rURGubzze!Xi^_jN83{N zF!HE{0K!es@%piIq(7-~#tIpZMq~SC=e@k%d5Z3I=PCoP)7IT}N?cr7*$XfmCnJ>-75jW~- zgJY6@_{G^_31WSQKAEtbb~drz4ZZ5k0@f$c3*}*e!3TOV-=il8)nWIUEXW#k3 zNh$=VHa{Q8^1ZJ3!wnx>PF*bL!{MA8mi%diDd%HzH5Qc4gwRFNtl+ zlo^oCcBZ0HS5;Z@6$n1}V4Qyw&Of>N%bk=z6yk^HcORe%U~yyX5XQ5!W6^8FEIUkG z8{8t}T1bLroOka#8%-In3uo-QI^k!96zw<9bzYZt3Pu-2Hz*{EU;X8pXZ{-8I`|Y+ zzBhP8>_`kjN z!1d?5`M-c~ynxO*h)F&+&GI^RFoSi(91a)qYJb~=3kt!gU=*@^$;4A+SGsEQeYhsK z{&dGjWKWD5AUBLfMmZmB|N6QM%K66P7s5sT;fqJxx{FepBHdOh3&#blwAQl9f;9mQ zwHEWq5RALsQ(%ea!?gNMD>#_WAE5!CFPCFwoE9x`q6;4dVOp#NHETpL6dBk%Hn~no zx6ii*(qk>s$0>NlrRdHhQo`1tewA~#WN|rS-N`4EEsMuU2}j`A#Kz$Oz%WnFSO0td z8hT|DttpiwQ$cC+(0t)MxkhlccDa-jdO4y!I0ue%3J3MTDJ*~KRJm@Curyaq5+j%!4>nU1t&un? zYjG#nFNGQZc3Mi+O|GgbmKkOr73Y2Bh7=z>@F`I6rE=)(MrzQm^BLrAd(|njSG^0H z6zMo}br+Vmr;NaRIE?tUtXLg-To4XEO}EKE?pnVT*J5`Ri+Ozg^4x{%cP@273OWK7 zR?u%6EP|xn|4TwWZH-p^0WBeG3vq~acY|cX(%r6)>A0}I_hS^JExHOv_1B`Soo{em z6(Q&!)EO&pvI{#UIQ-BC{-Lh4>bbfeFDMPPE{9mmMGom#0b;biA0gNOX%&dItE@c~ z0d69oYQJwlwmJ2j3vd(_+2D!-D~UZ=$?j&wy@9d9e$s(vI9>8ZfB8XHW?)M#uN95$ zRs2|2T-<>}F7W+M?euq-E9V5I@jLZkPJKLW2|H0@7ZO4o! zor*b2N-ERd_Zs3*Es~+hHhpg`WUiKC(uDOtQc?9087oEONh{Px?0CUfsDEfA!YuTd z-p<00jL`pL1QPX^jX;wAtdVR4CEIL-6DjUb5oG424Mt8(<`euo`~>j1ION1g<~DZ;}Nbka2#N+a5Qn;j&QSq$LJ&1 zqi!lb%psZJL?I4d9Ms>WrbK12IIgcp7(!qR$p`f(Kw;qk<@+2oEFwvqzQ6!cxDQ(! z42;)j86Zk#TX&Rlgq~r5`1qRbDG`{cCjo@2K)KSA^g9hAQBO4jNqRC1l+q|oXU7G7 zjse6|Ntl{L+Y7N5gem+~wvI^~EZMXnFo!l+ilJF$p)11% zeV~Vf8kb=$g?&7j;@{%YCXe!H+9*_y=cA?`z9r(8)Gu#I{WRD#Oi7Jlqc(O*4&bbZ z!wpvqAD)IJRL~}&zA+wcMkbE6IP7lac-!UO_Eo|ms?S|GJ{IY8DfmpVCD?T3sqM;X z*YM!*Lv+%-H$LhLVwS8`L_fc9`F+975z0}w(qd`$B=7VGm7_{MD3A8H8dR>WceR3} zD)|0y5AsuXDR4)x#7AHFzLc_Zc1Xc35uW4_MsOTP(4HCeYTM3v%6a&D{ziNzi;oJF z&B}MmX^-|%J7w1=zgqdqt?ZJ(K_nK5u_6QZT)nqT%JJbpVeGa_!B4=CR$=+(!z-V3 zDVtrxx8cLDWK7aGYq9jXjQ?Y0n*tl%)P_?#u(!%KN!jhjm$;Dv&H`&6GNlKX%5lmL zxw(~{+B5bY!C9zxi?ba)4%3J_mSwX)2#@7v$ub>6wP0fzpC4e45{-&c-VHRyZJC6H z;$<7D)x)b`Y{jl?Jd-9>Zf7>xH-paY;8=*}h(?2i?SVJ>2VWX}@GY)Q8zQcmjPsaH z7-{mAv>|F*Y}KT`Lj$!o>;;53^SFp_k_!}mF z6Q?5Y9fA{0w~LRS1b^^9uo4M+Y=bL$XnDSJ?~nz9s?BKRPywgDhsN_ z1`CFE@!OUD_6Ap1dUYaP=soalC;py1*|d8N*EVg6(yUI5CD$`;D#gxVvG?)VY4k+Y zbbT#O=*Ze5L)qu&w2wr~hiS$_VH)e07%PnNO=(uPp&G%Ch*R6-m4UsHsx=VY8~KG^ zh0o?2vCtap|Avnpto^(AP3o$(SmDLc$@^7TytzK1kw{=TzG^>=yI48yRZ&|u01hji zv4~chF_;_nTw1pjvc+ia#EMd_($BXa>BnhS(6Xfw-x;ovUcuKbgmeSO&`li??@azYc<8rwbH{g*so^|trHAAGI`Vhk_#QJSzX$?B_(gM;{; z5~&ZstIo}q$Z1NGdA*ukgFtiyGmQEkI1xCT@T*#q*eT)(;|hK5g+aHSd0+Sl3~$8I zj|Y572b^-6p`7@3^a>@q{7kfOIOAAWhu{DpKI$Tyi>39AaozS&SJzM(QIb;UPjjh# zF=Z*3hA-TsQ1^_S-~?@Y6b9Wr$KUkdDmAzq9g2_xXXE_auxt3A^{%ei?)yex0I|SxC;Q_c z5q`sHAOG{lvyIPRD)@ee?=|)bNj1Jz?dcEyf~`erHsXPf(l7>nak_+3@WnFr7fD(M zU-&AJ+7g0;I~D(LBmhOb2w{EguK)jPU)HqcXCi$N%71C!zrKg|jkI9LQe4}CgJCX* ztWSY{$~m`SMlcDZF&zV?&$HRVfFV!^;7DbOM5Dez=cn9oRV20~p`YuW0Qpv4D~~#dVUi{0coWB)36JhhznbAnP^>x+FwhYxR?I*W2?qmJoPRIGRgC2NauF8Eve=j6>lL2rvcafx+kWN9S zQiF9U{$J(SJ1UQqRkCl8wSl9jLI!IhT*)22-{2=|50J?$k2}S z%;kjs$$?~uEk*Eu?p2(SUx+^X!V{-h9jI}2C`$1qzxId?X30)om$qPhW()P z9Suuxs_7&>DeqbG}%D%imOly+}~3Af^BV}NTDA`#^l*DRv+F^u=sLbLp{X~flRgLLlRKy6GQE$vO8cDmx!bn z;DcHYzYjEynhfLOBS=I|#?(3*t!6Zt3u33%QuGUZ3pt-S>I0V-T#l%(#?J-eHz`Ot z+Wt=MLj38(AM7oTjM8_h;Hwn}{J~Wc(`uL|`e6b)t3Co`>I8j1I(KF5!eLApcg;6S;4;BSQE_>cbLY9nORpF+r48@d~MaY6gg z4;_#Y_9#V+j?sLiDSbhR%QjwbcRHtj(p*)b$T6n-Yb z)z?ZN=mi?63}v zXsmF&hX-xj$_TH*lP(I^u#gd6X$n`H!d0elc~{uwD5Eeba4`kS$0qv5ti#eN^y5Wi zW{R0@En>Xt8_YTPf^KH?dyW?N{Ls;gC){k}vfit`!T8jFd>5H$&oD+|bE$LA;}G#^ zKoZuyrgF5#gl^JPf#PU9nr2$JR*ClBhOys8BtXY2yai< zJN|B@EH}g1`d$W4a+Gx?&DB5SSaF!UMtwaX^q%SPGgjk9T!cZMFY%^ z(Iym1LV%PVK1GG2oF#KPMgW`+FrGlFC#8(zpq)UH2Z1(#Qxyzyr~gWgX)H_V@4RCW z^s4>}C(4Oo5@Ax_i~k;@5cNqq_k^r$V=Q9GA@q`^ACiIrZgcImaGQAT_( zb3CjLi?5;h#n9}d_!Wu2wREez(p5DIQTP!Lq#2;4 zM+X{v5$K3i7Gq%)y=U}q^+%Xh%)@TMe;pXZ{^?}Nuz741xD~6w7#BXl(l3SuKTSpA z<(zaZl<&ZDYNB?{m_s3r=(#PQkBmIYdw3hjve3FJ23I6pI?Q6e&E~-J*nF zM-kUX`9Q*1mGH4#)B^}hRZ>5h1{_J0@$+c(b1dpOyBA*qrrOtE1G(x?LkA&l6+gap zk|Yc#iP{;?&LE5tyD(3g3H4Ne>&d~%WD+3ed(0D3ehX1}w9&uQtd7mSB&y^|gKMMlgyfSs0T{X4vwMxKG1KqIjgz6`$N#6jKz_*^vM7YqWs zF_#`~^(BaFT0HoOEaxly&cko9Ha*JVVzhqdZBztmpiPfFhwnIq|GjT^asJp466Oy+ ziiy8vhu4uR;#4&a-Y%^;%7rt>kQ7YKSeN3b8J4ei`B*`$d>;uvF2^5?v$C>Z|MV@` z4`nYtu*U+DR^5r8EW{Rmo$;H5K3!>MiLr5Zs;(Tbi7tF=FGa|rt4Y`{2X;m(pC{}w zgpc|*Sgkk83Fny}9$)o8GQM=4a>7pftZ`-eXzPuBD>B6pj~Nj$jD3ScACo(!r$O%Y zsW9!Pu&G)v)Gxo*yyQ~OyWp)~hAu}v<3+>PV(4ST4TeHCgn_O> z=3Z{ny|A*YL1QQ+0UPsm#sZzX(reE$uVF>oyWRdeXR$BE*RXi8;$hgFHr4PCFz@QP zV9NzY2b&gb%LV?jpZ007<+30}y*wiaEdS`y(z`ldP90o*e9(*UXwml+_m^5~6m`5n zU9{qECbJNYUpHDRd(=c_03@aV9gQHBFUHU97opEUYe@zPXV7op@poVkh8rPURo%lFzrAM<`6*yo6p9fZzX$?e1;Yhyf{}MYedL1nyu}zmebgc_F zUSRjNCA4#fBK7xAqr3s+*;7-3dnK}MS(p9k5(ajg$O0StPhM^Dr)*1V+2Lkt0Q@Qt ze(G}oV@*qfJTZSzQ&Kc63c5DrLM( z25CGqNaOmDK|)xn8W*#US~XA`sklF7b;oSOt`2+?u}UV4*uWZkN6CZ@8~7Z8Rhj)D z!GQZyQNNcdqge5GWPnM+;7iK|Kh?7uZt|xvqHg4YAIl?KqQwrv4^yd8cTidQNr$yj zqgYv~WG_cjS*iW-JqW7u6Dw~VnvlKv6Wi?BKCV#3tn}SG8T$M@}1=Oh9kV#jiogj=#k3=XRYwf7?)d-@} z$Wc~5qBhe~MeV*=mp02Ds2eD+M7H373_dj3m;&oHXaz$m_?QTdt-X|q7fIs!K+^et zQ|anQ#Eg+lKM&R9a-?;O4Sh(CA%NuE_D7#U`5tY46v)7#DE-OTCmDk83rI~QB(xb^nbw;yR0DkG4Qk>P!IW+$(p7iQPiXoZ z-X50>cL$?4|9sLzlkd@vkL@HCa-1?~RcVJlmq`GhK5W$YL77+&r(^3h2h&$mb=A?> z5u~+A((HZM5FgCMO|#eRS821Brp-_!tnt&_8FO_ui#C6LXdeIg_Nra&MN=9ZI1%0w z1gqeiAZr6VOCTe7A2#C?4B-{B$4XtR6wDY8vp2sWJCm1fAv zJ}oRyxa@EXS*1!G**^(C!Jo>U zMjCaDpF_lndc@NNj2vg0fYDclH0m_5=ZB7FJTdA}HsBEJO67M5zW1^CdgC}~=wDC+ zwqwfE7N`A&@P z=UxU5U+Ned5NjPu9f7k*)i6=B@nbf7^h~CFto!{NZ(xGvFE&0Mz`PAbktwA&n_i-9 z_UNmT2tS^HIYmc(}*(>ER@eewRewVg~9lNjn$m|A%uoPP8M%+u+pb zwHQsO(^tZ|^yyq7wx3Zwnf7=y(UqnedWtt_Hmf@zXq@4SRzY-Ik%SgRI`rQlQqJ)? z9L%)H_`+o%5F8evUn#=ONv!;eM62C8FU^qf8hU}w=6qz}Gdccp82>Zj?1MG9XY5;% ziN=sz6+{f{&BpIaGVveMD={9xRMd-=zd{?3U5L{kLPar&A4xw`0XqR7zLyqrM?w(4 zix}HwU&S@i(D8-PaX*QvTC8X}kF4nT^ggDTz#EP+C(7BtbEbC_t7W}3}wOCmKJij_L z9}oOGM~vlHCj$);p+O)&wxZ5FpAe197tD9U_Ui-ss=9j!fFfO>VoA(=VG0KG{uLRq zTWQ#P=}%#0_#Y-b?B8{_Ju6SY{zZ}!eaB2=$dVVR`n3H_HfqUB84w;6b%lgUJB`I& zrQ!ISzAG`Q#8?7bf?uK1w{$R(&O^N*4@r9n8vuaZfv)J)P!Tka137h#;$*u?mlP}xV*8Q4DaQ}iTAu|5g&a7R z#d*;Iz;FtE|0<&+$#gWZ!tpsGJNJnRWj~4u#e2nsIorg9?4X!1^&etF=Br|Y{0A{1 zWf`SxW!da&w6YNm1DiFGA!MjES`0$&^;e zg!%|(UX}W?%gv)J`eYo=^QdFC{Ia zRlJ9n4x#L}1L5|ZsIPzd66qf@(y#B4{t(!{X#SFs{&AK*+yJurWHDJ~{DimFSzBKn zI3Km(6a@c$Dx5_8P^NV`eq5!qwW~kUxBlQ5Qj+-Zhg#Tp39K9tZCu)DyLttFuOJ#-Dt1X3LcM%5 zu=(-uP#;O3CZDCR#I@0*<(1g3 zy55REKC4CV1--`d{RiuRMlIR~vz&RpBDOv+Pi|R!#rkuIqduWDn|7}7z1m4^KiJpN zSnk75;lnnl7I>Hug73<8^uzoN%E!tTXy> zlJ1j{VtD~YgR&DN49qARDw>r!+y)`J1%qOxKm!E9C>5EftgOsqchAYZlo{T@Q=TF< zvpbpDLs(kJymWEC@3r@Oh8cu&zTe;H_4|JR=}~6wXYKpiYp=cb+LuSH3Jn!fk?$`P zbce9T5zvrx@T9-X{?EPN(y&!DJMQM9)(jTeJwm7)=RFq15o6fsOs5=O`MXdV7z$w; z2~!=*saPVMrOk|bB1D^0d?$#Uu6t&8+o_0ZolYc7{S5i5B!5r{aX$bV2~jKJ3*7X# zA3fcX6}=aSlARNR@RbiIo%`Dsg!9K`bX6r5r7<)@{=353)l|r841Sk`=)sBghlan4 zV|BPtj}Gn0#p~A|r9#djQ7BgRvLP91RS)IZYD6*R2E$J%6n4sWVGkJ2uUT=Kn|5Rm zXN9BPGqv^n`rrt41gaqvjaHoA1*?<9^IFJkvU>HR7L*Uwh`=Dc3%NyqhKMQkKU7?Y zd^#J6`)U7un;J<4U?SphF?JUP;efGW=_*9ReR0YZ(BwO7gJKD&oaU!k<60|lyZwX+ ztE)I@vk_si8Dlq$qfb*aSsNB&T`x7#+7OR%XK6dUE%mhmXt&xoOa!fQ!%4$LNPiRh z@ZAYuwRU~(F;aRMCiF4Y1zHg^uBagMYKkt+8OSQY5%N7}sVL|#)|W~e6E zRgQZe=|)zC<^gog)CZ=x6HB734M9_|zK$(Y5YUbp*7`G;xS}su8U|xQykakEv5{(l zSO{MMFYw6qZOLNZ)|MQJ98kknuaZ^vH8u*Ebu1WXrlAA!#Lod*~y!kz(SD znjIIy3^k*9cr=uxu7{guNLKuf_z*XdZQ6~*)CZ6bZMltZ**qNYv?_@=wV2Rt3Dor! zZ9fl0X+h2OB^QehM66z2{GeJM< zZ$|O3U+gjXL-7k&95-4?%Z6<$RWOinuO%sCMLz}pE43AYEj^dYsax-$AlQt9a9syz z95mgE*028nn-fg4a7y=x--oApuZY-gd0Nca(lC9Oj>KHKJK( zyqZq6fmf7Pn=tj2hq)FML*#G@Uo=43V>#tm^;*P2*~1J=yQp9;Ir%q(L+ZcCY4XZW z-AX(1M9FuBnVd(%)MKLPX0$6bTF?gNHj83oeIe9TpA_nejgrQyErLqFhgG~thpelS z7||*?g4W~5;0`N<4ypr$j5fu6TyYo%2Wnq0dFV;++{$Mm@g9|5hfYBxqFAWApc=~Q zYzL&(dsc}Z z=Rd$NhfJHJ5CexltPPdaXRNL|0>nlGNe#519ci!zgpf3W&FYN8KPenBgcGTbv{tnU zv5IITdyBSt3YDuqDSRtsr+$OU0tiHf_15=jC`4oQ+)qM-BB_elcmUhlgwclT21HzN z)r4`5DYX6o>zA;Db(_G%z?Rd5>rE&4E&Ti)f0S6QK%ZbM1MUETA!mo0PlBiZVAmFw zH}9OFfU^)#TvUZ1;?f+tGC*x%6L~G9){}r9Zg-kC7#ZM zkl~#qRy_`1qj%EbkOI{;aj<5bN9CeS^}k@ZJPhyH!0WnapqKrf`Uchozd3Gl{^93X zh+mx*eYyqm2)p|*3PKC0*ERIRetu9$Z*ko_ur483Ed__r4XZ|zYYGG(Hsq54b2Uf2 z3<&na0uC;{L`|S2S_3SGPwKb_p7gEXY(lDkRbi^;u0y35JEO1PUU3E*|D zd=)YAx=y}|$avi(UqyVpw#e621n|0BzKSI9+AdxzMH2Xm)j_;E?#5!AdOk@27>MG) zW){~7wB&l8*y6lP1L5$g2;eFaL)DY4beVB4{o+u_Wzmg;;UPL#{kGv>TdLY9C-vSl z2w1g~T15&XIS=Vh{5y5igOrv`m@Xj*Ro4h%L^2&Bqd~}W8ie4&fk!cwPNY!|KR_AD z*kJU{!=r!DeJEacAAmnjqg1d0U{T_h{xC_sHPZ+x{d~g!Lv=2MC6=TiKaKc6T-j0V zs{}9Nl1=PMYFH`gXmWmwgFd*=GYw%O8-vjwvA3bE<|KZ1nyaC9{fhL>)rSq$KfwX! zo)utmy-mzdX2Lr`TZ!}ACjuR&(AYRr^BEinNWFss#&`m{X6DEcpGFZd_X?!Z}o_=rr+3^ahq@n_1YVR58t0@+bwDJKlo_Y3?? zh}dz<(u^p?F-z5U;w;tj00l&iuwMjGv3C#MxC3qyH8{WYE1Iv~9N9@9MMQf9C-zqj zHDtIVz4}+^a)3k{vU?k&`%6Cm*O3EFGYs8;AU2oAi%c-t+k>AWp{h$ z&I0n7YLNwqf;=NOpa%9D^JM){5{z6ce2#-&_!1OP2iy5S50Nc4pkRzU!IB`K? ziYq3tbHyKC`ZA&|(W$c_+)?6C>zniKi;VX2;tcxW9Cc*Okg>ZHTNiL6?*oLO!S%92 z)Mpuqp%mz8p}>R}mgW^%TqPJl9}waRUF7oKMn%LE;tt~?f(oarWF&W98iH@?Emn`Q z^s!&4xMnom3Mx`3jji7LGmod3JIJkx$f^PzndX=3Wa|9T-Z5dMeIKOy!61#bBwAI1 zMk^HZBWQnaM6{q;sUEN}4{_ zU4-6`mco%^{9+r4uyEwu^jktsy;*OlrFp@o-_X+Hd{x&V@IOre&bl`dmkXo?s(ui# zB1=f%P;7jMNdksWOB)iWel~|b@h8mk*<_X%XR`2BDl*WggEF}gr16S#w;yabzan`| zIo9fhv3&)i8#b7OCR}K!4$~>h!W$-p8EWQJKXWb)4wHOab3L+gPEqu<)^<$YPrTn& zL!&x9oy0Vh{)CGR)nxCQFqq{;&DBUpmR|JV>Mck|$Y@K$qcpL>B0H|eY}NaB*DkUC zbms`s_Yl%-bbf=@7K=ORlu&Qnso}VU43;9}#V1c4a31lmT%?C!xQdFf!4zN`-Ud|@ z_tm>#IY3et*Q4#oXWMDaY`;d?>uUDlHL>pP{Q&g!iTcxOz$w9zMk{tv;ruErOok1X z0889I|GmcW*mm6J9{tbQ?S|^5sOlz?P@+vb=ZVf)Nd$1(m9yAquaC&7uc6#TYx~#L z?3HePM4dUv(|TBen!^1WyPYHa{R}l+mr(hS(l-4+gB? zzU})fJHIC}fb%ZZ1KW@X#v$BtCf$abQApi_WdeK}?{wMcomY036QrigDTxjBZl;!L z_{W{^a(Sp*!UUJoRv8a+YV{CYOT0TF0<4RZUnW=bA93wVhC;ceA+B5g88(&$Hlm8~ic{T+5c0P06t_RYPQ~zW<>f^Wo z`v4sS#SRjxcTBz1Wv{INL@osovd9Ud#eG$ArKHF~h!Jn^&>G>rF6BY4}%I z4Yd006ojUsE=_bupGs{vK>tKMNE=j)*)U$Vr{AfAUCo|WBrdH*mcI7CTVWBA_9uLn zYUbP2fl13GZ7RoE5puR3^RJCz5%G17h<-Wsk5OF>)t7+QWU;_58SH^?ct`e&R&{PQ zN&=y>*56Pw0Aau^{m{W^stsnVp~!%b5~SnTp{7=IoDM%X_+dH6>Uz2oxzctp5ddkAxCP)q4vvD+BpqjV%r`>bShIGT8Wuy)0tLWFuIY)|pK_%r7>XJIq0YmT6ut95c4ZG%1nQa}(9m`C7YsNREL zYlDj@j@h)aVT90DNNq|)aPe_3EOiI??ZVna1guc6NMZ;3sUQAKGZwQEd=#(8S*1z1 z`>Y8s)faw%#6*WQ)ybDKe5V87b94TBy6j79!y7WQ#gX-$&?HUn)X+}qu+Zvm84=M5 zyN|p6du%7f5s_c7v}FBp>bLrX!`oq+x9Z-en66UTQ1 zKpdN)vuyiDkC|da;&m7ue#f%;O(ei^!0bW=`wtt^t_!uSFxBDMkR>baawrmx%Y)RY zDq6t(T9<-2G7l5Z!QfL-;0?1ruDxQp%X9_@YH(ztjkbBUn$QWh{fJZ-2b${2{IN=C zt}_RqN9eX*gt~&ZqJQFOVJ{PQTK&dixOQdsBNb9_1^eSh&`lUdl{du-$1^_iVa8h| z5kR3(Aq^bUtInPw@>cIageB9e&XTA^f2>$ib(S=OO_9LkeR1F>vIaGYZOw0?=O}ey z5u0h`$5d+Th4hXqPSk-Y1uQSTvjtSHI!kLJjv*COtIqyJu1!^EY2nvltvc&R>C-YR zrN3s=wQx0cJtfEOsiL%AFda#Y4RW;mCqjC*#3l=bO zOMI%j2W{Nd+CARD-unPC8Z5T0N?0oftyW0K)_7g;$$p^@6Q%~=CmW&drs~NE^6EWq z_eot)8TKAKDb*ZjxkV|v4qoC~!RWnrh=%WMU5vU*IoB}m??|BQZB0MLM@=jZuhT+_ z#nmKq^@d8RvQLeKwI~v1cnDTlq5g}RzUx7mzU#tkhEnmnugRzDB*Nvjpe`m^LRFIOxIW;{=D2CM=X--}8sv^v`j=BL*B$i|Llw&~G!b+`^0S zK?@I;gMq?%-i`GLi>q9}nI`L&s#cod{d>)3VkM~<#cnU#MR)Yo?{anoVZ^N8-v4{Y z<@B{1jJg+M9bs>DOU(~B=Nw{aZpo=n2<}==Em2(|xL_NOseVQ8&5&L7c=yFAHrp#d?34P+D}Q_&g>tdDZVhT!fJTi~f2~AwbRXiq zhI&&sy=mD`U5M;X>O#wgVBy*lL9P3h_zJRME;SFn&hz|mf)4k?!t0LC@5-s~RHCqg z5inJONTvNhnyke>vhKMHpi%m*9|rR7O5ihJA4~DS+|_OZ+MID42(K zSnP7x$#NZ}-U|fo;Gnj~Xb-zTF%9I{IhSH{`#TrKDz3SHg70uBgT9LKR122$LaVoz z9diEWZ+iPU2;g|O>jBBnS)WbG!pEm}K$?C>LL3KSt_xM)SVDVE4q3!*w9BE0pH%M$ z87j?sd|vBU7y-hDZ>NJdv(fu4Szy>>m~o9)e_TYG*e-FnQJc^8vi^w0RY*{LG9LHi zs-L2QmWG$8dt>V-6qfJQQv&q}EstPVi=p~4B6Ne3mPqh5R{{79I#_|FzKKFo#HA=U z-Hf4{Q4lzy)CuGOzwQ)>w~7%^jl56bh??LK@9%7eAe1IY&Ntp za=co-R=&kr+srgpiS^}yY8YYL%&~+ao1dDnfTlEyXA2cT{T3vn4bAFNgj4%b`=C>z zt4c#m=SK^vN1!5vW;-AitP}ChIlrTd^=Mv>7He!ILGe9Ladtu7jI`=8^uT_;OPzxj z!RbE>jFYo*Uu|N&-J+@}8rDS}7h1DU$mrXS=+yfW4ThW4-q+2v_5i0w|DX;<*scS# zheci!i&06aKkpccAg((Dp#k}m0@P~8;AGb8TFn74#eJz3pg30O+!#~L*lvD5O{2vP zwXO$+Qw$^+am0FW0xDq&H=WeOSW$Bxg>>CHtkpc!D6>IwR|u{&53|BLS`O-w6^Nin zQY?#5TC^-eY0-L!+#^C)0SqvT(a=ys-$!XRC(9{M|LDD~W-H!xNt-93GD3u*Yt)%7 z>MGC;LjE#QBFE*x;=J}tg231pM{}B-CTcrK649GdK_!GgN~@UIV&Mmt94KmW;j>pl zRU{FnC9fvRv|AuSUF50v_8bOJaW_ctc}mxlf=`GHRGVC?t!oO7Yty&v;xQDPhxIii zYvHY~MoA@((@nK=hjK|NYKKnR_=o)h%_2`qOHdj+*~N(_*CbX0KyA@_Fql!SXAz6( zvC5%UBTVVsC-JE7lUMYt@AYL`z)+?0MFi}Y7u~}GR&2TljBd>wT>)kx!HuwFi5pKg zn8ABL8aCXia~`Uo>w3S@`)wx`+4(&-)9>}$p$;v@5cOLW7H2V+FM!2pLzxagivu>1 zvRZL%x2Dj&faR+Xqs=SPWYq4o6SYh*=1p7UjS$ModRmpb*0ACF_#4WC95^o58NHXk zCkT_oc^F-E@v+Ap;m++kQ`|=tU9jolD|&5ZyEmOg_FYvb=dM5)Hcy%`iSBRB+J$4K z*ajY;l=&g?Es%3CRM|u9rab`PWNwU8u4_?a}4Dj)EWob{)ojwY@QK17d01c7M~3@ zUaUYhJWb;ZIK=Wh{? z3pSIo1KVLR7#1yx`GLi?GD1Q6!YYX40P1AK-W&nC%8L=b?Prc%U0azHf+)fsE9s$; zB2ttbUn}3Zj1*OTBl$ z2Gb8)Nn3WM`JYY1x1=_HXfibK!WTM5>Jwa;y5eHHxb62jiMm#te5yddwPyXo3lE3z z1&*;`B-MhwtG)ytik3isb4=SD2+Glio=%CQ^VrUQOzpfI3m~{W4J#c;UzFw=lAb~d zjXvq@2ynK*Y-SGDsMYC45xM`t{;Vr|0VWSWzp`f`cY5A;LPKW<2@wpUXb-Wk3(U$J z&c)^~=h-ma`iHB!V3}AjEc!D!ZG)6!O^0;HaO(X=4Azmnj}dZg(Zz!9URyTNe1;7V zd?>(UWEoA7Mt1P{Mr!VD9eTfZ=jkZJvNu46cSzQ}ML+6LbU{)u<00EO3Y{wg8wunF z?gCO7d6L-h26@uw#MG<7hpr~Et+B;m{EPlN9ApD*{(K!7-e|+p|2X^83$GuzegB7N&DX{tNd5ffR=MNxS_jnXv zw-{%kaRT8#kon+AB6BsG7*xUJN}Y$b27CexpH?Sz&-;4N9{LmxxXj1UM4N_OWdrKW z{qbSvaOhTkq+8X^g!tM{8WymjnwC{Jv!7wZ{&#ST7y_UH?hI5%#j2ANNSjLG>7ar!N|!cmvhET+)pCp5au~zRFJ~Sc@zs-VnvRow*K#oLZxx~ zk4)aMdns_?5>yB|^->5|s~o+=AorVS>>8q%6-UJaVV~x!KxBcg>kiMO-Ct2eSAxH( zZcYRa{vE-jL|Psr`~?<^c(}z+*dinJY6=MInjDBg(R=0e{y{J^=?XA`X44s&d|Hfi z@EMsVF}0^buwBU8Ue_bbA#oRs(gbZPC59xjX({iVn-#Tj3i37&)*|06@~AW9*k&}7 zlw$@IjlW~B?z>B1)%m%Y9qs6PgvSeQL${>0kiN958wN8Wctmpt7&cn)ZC5Y`I4hekT zTq*^fPXOte2g`9~53<0SkSgJWoN?c9sL05$u_~S_q7&vu!-iC-9`QPiioqd@(_q|o zh;(SV8$QSHAqq<0nv6p_onu5|)znXwll_boCk%6+Gk9%MZ$vxDfsD?k(|CUdBaFoS z2GPnxTS~+8r2WvrZ}EL@d>ZU~OT+9S>H`MZ2h?^ug41S`YX!xH1}FN!DKM#EewYJH z;xxjW5y%|t=jsT-t=`Js<6sx;P9G0uum1|Hz3o^KF1W*zBkbh&;r0XQ+~^j^V0@22 zA3<->82=$~68QU=x)y253fw>y0A|BHLDlQJ$5(ZbR!ccgaJ0(tv$J1!ooV~)FpPf0 z3^_jRs=Ua4di=h#oZyHhK z=##@grTu5lwjlKaP^)t#!jZi`dhaH%o+8w*&M=`W$o8VHdJuc-J!%r`NK!UoqdGSF zVUwRYVMeA-EQqS*Aa~{AQ!v)*k!JmN9HOxUB`*!SHyf))HHWe1&M^$j>)4@hM3IdP zlPs=H#OkOB&iWluvrtRtc|nPF^9MN33ni1&HpIl%KVLJGuLiRN^l!ttfmYDFYaiJo zW@1DVoAYqN2VL0AS3RfDK0&&7pzOG{9w(_6lcpDM1;vK%q>JRy2MwwbtVsvy#kIV& z>P_?%_kC;OjC~UmOn1eTxZgT`3fcai2RW>+g(8Jak;0p2sPHwIuwfKTPObhdr6CuC zM-+Mvj7F~s@*PuWWm1h{Q^rBO9jXOy*u!GMM+d;Tx-y%R4;aOg+mer`=($iY$&&=*P&2IG*!KTx@*Io>D0P`6qKy7Uz9DC7ERB%EmZ+9 z8PC7aCt>v$W=L@wU+^B?RF6Fb5bIlWsVVCBLG#PSD?M=1*SzUTf!&YL>Mk@XZIL7o zR(H_UI{NJ9?T`)JXT(mP0eHuq6-jS`x`KIEMwBBK&66tu$*xc*AT|>UxISWqfLkI~ z0n*|;fL5PG1g8>E=J|kH=85&I#k=|$Sj9X02E4-{TV=_THKN=9RvD_H(tk%b>$K z*QYpe2hiQA@8+`5o*C>JK)c6VXzLrk7r((gX@cHRJqo3t5MZeO5N$HS-%$o7*h37N zG?}3gNZlh!YN%P_lfx{VEDl}7$o(o4P9#GOimPwy`*qSm^_-^eQcXmY(-G-b;5$d- zhfFhKWfH>Dj*7riNrixs8{$)#wi1UQN^z}@NTq^|%|Hx!w?!^FX|YJ`J*UCwB1FgM z2WNMV1=Ym9Dw4p*L9{*-rOnLhT-1XT)gcL)C@pGIqT3K%B>JT&?3{>VO0;3_e@Il6 z)JnW9OZqATiyIDUs1pzEU+3eH0ja}{m{6W}J1(}kE>FM<&HtTULGE@-gN{7PeiEJW zpO|rqdFUiX>^t$#JyrGZkAJ)Go4EU*80~%_W@$p=&M!OP zOS`vu%$n_2-}Gb3qvuWhb;s18QP1Q=eiHGI1$R7aEok`gnv&Bevukr#T(p1noIQ8i zH$0qQY)Sp&hs~ePy)fz4zqy{QZaVmq(zs>cfK!KO2W)!X9N0eMie>NKZ1CUq+S@w6 zK@ZNq;i2G$v<6k_vOQP)fa8d z40$np>!rWX`pe|jy^D;Gf7Wug{9m`9xblm&W9$00MgRSR(A$Pp9<`-Ut9aq!Hzo}n z`RzSVKl0C&$5wURIOWxAe_6M?>#YgS8Gp?fe4T#Dn+p#N{W>o!YvxtKi?i-qc<;~i zi*`l%&AaaG$1g2-_x^t_{`I!xI?a^PJC~_ z=hdJ$%dZ)A*_!pO>KBeHC%@hBVDVqx$g${7hGdMpb#v{u%b)uC^osNIK3UrQ#1#*8 zzCSbatHx*Q2flM;Vy@}0BMs})-nz)oUmp}|jIX?OeEz|graV4+?v||9$Hv{Y|MkVc z9^3WM=xqCf(PbqWh1o>~N=c?-E-5K0xxtuZ&v2BL*o}on4r6IqadAeZujs}3IBZd;C+5KjZb^#=<&co znSTrTcS~Do^i;U-QbV>o%Xf zEGZ)|CVq)wX7Y1ae=+T~8+Ob&^ZuK=z8^Yb#=ydw$m;(4zWUb*Q`MB|cYS{S?)+^p z{TlK0y+Ofm|9w(<-J=cPWT^$6?|pRkM&SJ&95&v%AZp4%k9wytO=$xvj`yl%u^GM(`T5Bsne~q;A5O@zO9GvIq|wl5Ou%*ojCjCUsa_X z?0DzGFTa0H{pgSL)~i`LRl`Uj-=|C}-<|FX)z zg*}w``vtFm{oOaWB)-|W!x5*ZwC_)e$XFl$Xk*KOjs9`bHIE3|QbC&gm3+JPl|?t6 z8arZX!0Aiox2q5Ra#6oe?w>Q~qan{cHPOGK;a^)%)J#7);-mh1N1PaOvR_T*na|tz z+&m=u!hc(Te&bNyU-f$HRi8cmOxo?g9{%J^zX12+_SIcKC%-?dt;A}XqlSIF?3z8+ z;emIh-8=a6r}ob{{>xJ}*F4j8Q|IW-D^Gr1S>E^TbQHl%9OgKzCQo*k3)bic(zHk?U%`PQo&ujtwr zFlDXtu6ZZd{Qae++m0Pue0+z0{OH!({t~YGm--$1YGBl4b?^&~+tm7pEqoapjnmZ&W>fc;fwGt8Qv&ob=J;K~JuKWYUNJ@!5^eg&DV(o`3Um zYcG9p=c?N$tbXF!2knjXrbfR!)1HpnVf34UXb>zUN>Q91)1ywziH9q}v?j5O8qLr-tJbR%7$rR?5=GwEBQhUh)oSyW&E4LkSyI0_k;D7r&*dxsB z@7(Ti{CUTV$2*MNq6~B`LQ9w1or%AL49VTsU;9z%rvk?Q(0jPGi0^l)?#5Lxnb5}E z<~eTn7Pu4dbi4NhhS}V1J@$sCL1~--xIYJ*jsQ;pHWD7QnAL#IfLj0$1AYz|1N*`W z!2N)iVR7vQU<{xG6WBDsX25*F{eabg*{~q21k}$*I)I6=hMfS6fEjrN_H#O59L@&J z#-iLxz%&>Swh$h0KVUPKC-m6K(2QlF5rF%#OqvZCft`od1n+aZUk0T8Z-)tDH(79h zgu}kZ7{G}8G1CAHbE19(tKIHq!UMhx=m6XUxEk;<;C{g0318!OM?j^hu64WT05(5> zcz_Xgh)3^$djM%4y&j`d8sKGs)qpX8F%O}ffQb)-o`7MGARhSx?gTsmc$o0b$PX|P zFbsPF9k}~o0^s`Ph!0r(80ra_wgT}15C0W(0gQOu?LG>axDxq-k=6rFAclGhynz*el2v@bIgk7vPE45D#$YKM*ez?|?CY*{`FW z0K?WJU%=IX&4BuiC@=YM2E72QF|7_hAN79=?E~lloB+5QFb!}&U_PLJ3+M(|4Y(4p z8E`$|YQQ~!>j4h~?gacDFbpyw>;j|aP>zhC*a`&s5iDJ@BbL#0b>p#AHWlU4#2ceKsUg!Pr)yM&47micLM$nsQ(Q4 zVKa8b=b#(GFAyIv=1b5Ea3|nudjA^v0`5PAasnRy26V^vPsg{YC*XLJKOEvP6B*lNa#}k* z<%v)EHUqy2_vA7zSxN|hMpMwH6q?|jB``EerAF>g?4&(1>Liq55iMWB7B1+UB z;nK0Nay|7OPdz;JZlrWYf2kCFPpOGMx)bqoF@7fz0`2J3C5ITvjfeR0FqIenXQ;fB zT>9F;xgqh({Fmw)12pmrItGKoic8(@I}yH@{L!yP?r1Y=L4?6W=_exIiUn@>uY{OX z8|dse#iefym==;snGjNj%ct}gy4{ng9OlM==~NVFziA=yo^CJrwVBc@$48hbBJ(lj z^D^-5!2dva?|ejM2r217`qc{PDGKF-__h9MSEnopwd)D^m*3-dWBBwu)E|OTr()m* ziwJ%|f3Kx}EBRpr{8z#MYELZLzp%AI*#!J3!h6YqIl#9Aznt)*-BX18sMR-u$s&nM z0;t`q5zcZi^hM^UWh76R5->rIl$vNS(hH6E{h!4X^+&wZh*u+0 zl0TyUNV<5j+nqtE9(1E}j(~p){GTTOKKQdXQ1t6~jj<@dY{ZLP;&x-0_dJBZ5BMnH zuk~P&2i(oTTY!IsWCQX+@AAlj?qMP^Bo>(vU+hHq-Ov|>G7tW449FsuBHrTBMDoQ^ zgj-kZc5kEnME%gd)LEirI|d`abm*By>`(HO=n?_^CgAO!cvF4&DG~lb&_{)wpfGr- zesh2i1%4zUy!gTad?fH!Q+wY^JowW1`=63jm^HF!c9T4KPatd_6eB0;1<4_&{iJR{<-QN$ zjmzBb0!jhl6Fj49qd)ntgnu#og}m@?FQY8S7Wl7f!n}^+^Fwss54;Ne5Df`P_@lrp zm=}yAJkry~M?K~nk-&?&G`ADwe;M#m!0)HG@?p0-nf$xUkMTvuzYqTF;NL|4J<^W>Y9;)Qn5WESdSd(~K?%9w(qsJX9uLsl zSS*Ng?MFNd<}oWpx{dnUfGJD;U1)ENN>uI>@ZSypU!=dTRzJlV-~zwI%W{QbUbPnU zpD%@ft=Mq#KFSC=-U}byhj7J@Gkp=1 z9!j6)d9hFMcsZ#yz!`X3NEFqeQ7`(%diaO0q%^!20bi3abd{5yrLRv7yO3tUqvLB5=xKPa7f;{~(iTmTMrTS$$ z>W-p(X$W^3;kHrzMfs@Tx-i&^etSzJ{vloyL#Nz;6Ox$RRJeLG#zp z=iKh+5m(YRMUJyQ$3KKyfpAU=C-~e~A2@JuVA|{xl?gzSiv??2#L{oKr5UZ$82mW4ZgQX34GLxZg(W%MLlHr^}t(t!S4Y+y%+pp;LCxBIm+`;`o9C;0X&HX?IC;^=Hmx} zXOSno2tNY&_FnL`TXJ_V_&LD006$SnoYKZ0*#%Am-!osb6C4Mgnnio4Kakx(1^x~~ zLQaZ#2x%qgkTF2*gnuOF{bD?Uztqz``fp-Ls<#SEiO7CNb_XNu1h*4Hq>FhA-IxyeLO!aePUx^I*r!U{ed2VKL^5DU|$)`BqG8cA;rGK zy`Up~I`H({u#IwgyIYf5md14|MG|SK9T1z8o1n)W#9IaX%vd28C}GSyG<8RWlI5&M zxJ}zwuNUo(Mq+AiUrgXr4k3?;dZ3e(UV#!?%fuw-)h+QM_bn2cUfB0N(<< zH{TMUIDl7yrxRr?$B8&ILQ=c+EUE|D%{pMWIt91Z&oupl(%C}k!=5GdUX)kx5$qF~ zRf@R<%`+$+va_vigB{Y74uq_io}!2SEPQu&Kk>G6P(Bgx&wbbJen^y;%6Laeywt9> z>7D2wDG1l`p4%NN!jYagH>5)^2W~1yKK$GFxZU^5dW&&=otJ+j{NrJFDWm=TQNHWr;$cd8!(m1N9m-%KmB8@2~asi|DQB4 zFopE>SyBQ3P2n5}R}OpOEqBAoG4Y+^zjnI3vR%Y8~`3;dhl zpDfBv4Kk+@!#EA%lS0-J^3Uf_3j9fdKPm7h1^%SKpA_hg0+^+UNAv6XuHpNFqdxKV z*)5DF(C--ff9~sl1;2+1Wdt@Pevjq%X$-3vKEUuXhQ6sO%IMJ0+)z@E`gq0}L+k#N ze*i9r7n83(;_E||cm?`$Uk|A;^gJrneubu7^lapJOtHmd87m>CoZ>lnKtesg&*p@F zIV;T`p|FwAqs@alE|%|%R*bOi(8ECakn@9SLp)g86py9_h3c*n@1*n5bDRsNk&dG) zSSl6I?4c5B_{gi}yEbQ!;)G#J6OWePSvK1Qad_}k5e3lbbe!M+A&aA&;rIVQVwu7J zrQdizE?AHJ7I6F3GF;AZ4Z{r#TNxf;c!c3ehJi6M;foj!WjLN;62n;xa~LjQSj%uZ z!!-;yFl=RbfZ-8_Cm9Bg7y!^b(EcrWf%&550 znPqwT*&`h#b_6TP9v?GO_-B=tjm|1@t;LJc>(4|4w1Pvnkor`%ISbya<^7H#GWFUUU9t}{^ z?|)EkMPVw67U}P=@rZ7K-{i8JMn_G?fdS;x-(O=EUAW(5f83uzIE`U+5jeU7RL%c? zMI_pC5jD2E#8Wnn@pJ~1O_Ub}!S^Eh5ij`B>Nbq~fmWqy%*R@FK4(7G;%TsFdRjr! zIlfkmBYnhIMwKoqaAafxI!YwW)m-xm%SPp9l;$dCEjb=b@COMBMgSCpOM$WTVvb8R^#83lG*ZZ_g-Z_21FM^Q;B zGL+!1EO=4`N*!@UNiquZvXFZb(!|dwk&#R^vlLOYiV6y_XC6WAnPu~A891di&rV5- z*F2n%(cBrCnI-lG+FO2Jpwe4<$okw0T@OA@PjFAC*muugy0#sLd}a zEIl4!Dqj^+@4>5{@@w-u4bn+ZdbIj$`Dkz={Ali|y=(JF4H_vj9x6X2rInv>^i0E_ zRyTUr=Bpai=Brx%T70ekcl3y_&3`qh&6g=Y&AEHVw*yDz(&pz{Ki8m^4}yq?n^SvL z(&6%suk~*YuG2E)7+QR7-aNlYd`<3XaQEXJk%1OZ!`|H^z9yG6sM$5N@LK-b`$CSd zwV(1a`!J-vK;Dnh`vvX_{89b2_;Wh&f=7d^dt}(-RcHv_@s)37d<`BR#P1AxkW+aK zc#5y_hoW4?AqfjP&Tks%8UHDGc*hUr_@M&(-{U{uBfgR28##W@{Cd8>*du;C$B*au z8qmsb)Eo>q0ut|P^^fcx#f$$ z*!$Jv)+v{G+};kI!p!cCgj*+eW%NJ_yx2K>j)zXU;v5|Dy7jnTv{!<_pW9xkz*D|aJU_#$cyjRTr*zYk%1uuh{s_O0#|KRg zIDw~h*7ZoIuktSw>4ZX#`0~R_fj1}}r{VB=-apGVGZZ&5a|z8I=DOFM+b6k93~u1HZZt{7b-7`77^{35&B1aPH_MJnbo`@)$pp;RColA2a?M#%ulP z2;)aGUK>+?0$%U~6O5Oa6&(ic-r6?=ctK~fx#1!Gj-JaI-~OO{*ScARhHsGgUnEe* z08izAj?2T-XeC|?-zWn<%He5GIptgIk_fS<9?m(8kEf20N9>^o%;WTz^`Or^jBoCd z?*ojF??Int#-}shNSWYymhqK6(s>>D-snbO^P}>oJ|g26NucZy=?qqynEy4s=RHn8 z{c{;G1Ml=4=5!P$jM!@r{Bg#o(}W9;INJe8XA%9BK`@D4CGcAMIxqm?i+kXMfbXsR z7xaOrvy7BZ9jBx5+qH~u>Vc2zBb}T3z$bA!D|)1J8}LNWj^{+R6tNc@PC8>q_|U&e z{JRn;^R)1cC%H_|LJj|-d>4D6;j96k>ea&eR!VQ>A;z!mQJ!ZQzp4lR?*czix$i5P z0J@ua+8Do>@tQpR82H}ke+YQ0m+>7LU#ssQoX+o@jz)hvb4mS6-yy?m>0b;7r4zM7 z;>Dg?I7c!*ey7CW&jrPI$a*c$G!CE61m^6OoIb)AaQJl}$n;}5{c_;_{C{%y)W?Ee1X89hfU!!k4Vm{obge&OGFamtNKW%nZpNss+%NYJEx;+=~qg8GKW76 zypi4BpkH9`_)CGOg{7X)D1isv#9@{0{Y3G7gr_@HdP}EJ;Ln|ZIXL~ODw(r3Pp9+W zRNpAO#4qQ9(b+G-S2s(9xDx?Rx}$~g>sX(;mcu{K_*92PT*&x!0?#jAV0$0<1AX8- zIsJ}OnFtqG`8VTNl}P+H&f^cpS2DkeGv)9L>L<&8oE6*GID9zc+c{y4e}*uAUAYX{ z!2B?l@vH8W_ySJnW-Wa(bm7tZ-BiXKdAuGXy_Gu{-(i!8Um2e%@ciNh?$^?9mq@WU zAI_zWUpHOiHTth$eA6b0e@g;o4ddhAlL&F90M1PU&w;$a`y4)<`}HFB`;zg>Dv4nB zRgP-mmq`3=9RBw{(hot$?2Vs?2>f|UIu~5Ck6p+3_9tY*k(~ZG;5FJZN`Pq`e%-4Q zE6x(ZIfwDbxn2Pr$-(%JXC)$z@lM7YUzZ4R)(FmJ8vbdCXSP#TFn-l4i5F+h;CxBL zbNV+(Z{;n1h`Tl53>qNYCCV!C+I)X7!|;Icc)xS_mcPn?Y?fB~V`vxsk;gA> zzBY*Qt2lkFURMe{XXXXQ_JL34bdD#>z=<4Z7UNs?N<=;5(=|MgN6$0f!T8EK5+Uwb zfwPM7>o!aLKREoujE~}c!x;a#mVT8)X!FO_j8EMs@#0JvoUdqj?l1c}ooxcoFJ9my zEj*X!R`&aW@$J(kLfm}==NaIsy;^u)tR;1}kMu8qp_AGxo*D3ZjzDM43EwhJA`UTr zDC3**B!bpC=o!uUcJ9BIO0+V8@u7<)ViNOnGVoel4l2MM9KMnTKSvX1)u}vDnKJZ5 zj&u))H!?qK_zRiq}-zf2#KKwM}Q@Q+Fe|(AYm7Kmm=liz6^NSbwq7VGf zoK7*P6U1@OYU$r15oFh+XTU(FKkF4kBw7h)d=v}*NX8Elcz*E$F&w^$^Sy)p5`pin zUr*-n9h|;a&bf>~&gB`$>EsK%SBC6d)(8GsPA8T7(OeGy3gg>Zj$O_8w-|q%+hsiC zclVM0_Z+@yn+zz<$if*2!PuMJzF6R)cfBgZPvZ3PO`~4JkLU0$JpTT|;gc9&$>nFW zuQ+?(TfR9W{JHB;`J9gN0hzG4vxA(prHQsOjmlE8GV=3n^9qUzZKaNk5(n*GD|M9N zBLrEB&6bdM%XC|cbw--ahPOnYw@C?JUfD&qdHF?|8TmG`>B*K+R<2+VQ*pi>mq(8p zA2mK!ArD(#b~)azk5c5msqC_Xf<;zb)or~CEC34BIm!hcC zv#^;9PFrbaQMqvB30G#`yu3mO0@%xoZJBuvTVAQH2wS9Zf2rL@1uRui|Dr6V7 z@e9iG9eKt1iu7 zuFSJ#7up=S9T{~*Xxluy!&c_VvN`e!?8-cQp)Jc^LZm4Lo?i3qh4UP_!Y8{>03=u_ zE(4bd;C0ma=y6d>M)qB0r4HM|JV&mrH18gnT3%^snY~2BD=y2l&9^Taqp-tPYQG!J z9v7_@5iikW=pI-qFJ7XeamOk4(aCVv#}N%|_QEXb9X)oel3$dOZ7)G-!2?B^cPZJ0 zD9ZTpO13>G15JiH=Pk%^*eP4`h*gU11-5LvXf>tKzR*VR6fx5djwn^^h1sPF-ELc2 znvYbAN^Nw}Ib3wvt&Kd;A4=%LTU$|%4Hw>u*WzeuR!4p*(LSRb4jbiEmMK~=E3Y^g1Q2`qsOYXTZYULEYAWNE%F%TX?>0N7hG_H+`VaqDPt@czu^y!@V_~aDp#01+I+)g}Bp)d;xfE3{{UT~HiuLMoNu zBnS_stk82UzhFwFT2O>uU$oF(GDg9@>4@c^c2Y`kPxAuExeU>ur66v%peIo~X@PAz zx<1RHxK2n!TS zkh+>EZ&oJy2=`S8Mw^v(+;Y3-W1lSOm+hNofgq?aE{A%pFKT*144NMOk>sr{DX%ai zp9FhmMo9^ZEAuWY1b0e$2@;dwm6=1N2zDpxlN`3qEXEdH$tT=aWWiQoyPZq{vQz?Ck1UUykU!0Z(gdEsMjF2{4R(S@sWkxc35M?mqSE$UTJYrX+|cbs~{Q)sN#&Q`F6?ZNLR+y;+08SdqorUCH5wE zfhZI0YA?s2URWesp~PNj$8PF8Y9tiNK0b!nswf*BjwnFmeFnyT&}d5H7_BuyI(z^l zBd@R!JGp(U1ac6sl9~8qJj+SS?Gyajon3c2jX_L72(LfULjW2|6!Do?3`bV+>pR-YKdYpkUtG`@$51*t|D1cHKh zPN#)W>D^^$0l_3BzeQA8R+KF}&*3%Pc+rGNEtregoIhAX9onlu((oZV38}G;vQkeE zCYsu8*i=s*D4y0^sj>>m2MEiuVi8>9DYSW3L9uVW3D=Xzs83eDJ);CYBrDghh(7IM z4U!0;AnwVxiL4OVBc(lKs;940Qbom!ByUjL%haf+2=aI|F`>{2rM)PtM+vD%Wq}U! zAo0631!|AdvP?%w28I-L8?;O>gOm_&LhK@VUV&&x>LVJen@L1S$>^mHsAN65$NW4@ zCy2(}k12Z+f6RO2BV<)kVNTvWQ8qLw2qQZ}Mm}1dutY|QMg)=p`5B9B$gHTOhiD~L z%9jpc6p~lINXl314oKdkd_F&=fipbHSLg znua0NM({zQqlfw;dbbqH=MJW#UeL5KA`9)JyEEFR-Y)ca-#UnP@0NDnQsaLC7 generate_interface.pl -csharp + +VB.NET: +> generate_interface.pl -vbnet + + +Java: +> generate_SWIG_interface.pl -java +> cd java; swig -java -package axTLSj -noextern axTLSj.i + +Perl: +> generate_SWIG_interface.pl -perl +> cd perl; swig -noextern -perl axTLSp.i + +Java and Perl both create a library each called libaxtlsj.so and libaxtlsp.so +(or axtlsj.dll and atlsp.dll on Win32 platforms). + +Note: the "-noextern" is deprecated in swig 1.3.27 and newer. The "-noextern" +option was required to get Win32 bindings to work (which is why is has probably +been deprecated). + +Each binding (except for Perl) has an extra helper interface to make life +easier. diff --git a/libs/nixio/axTLS/bindings/csharp/Makefile b/libs/nixio/axTLS/bindings/csharp/Makefile new file mode 100644 index 000000000..3414f8562 --- /dev/null +++ b/libs/nixio/axTLS/bindings/csharp/Makefile @@ -0,0 +1,35 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf + +clean:: + @rm -f axssl* axInterface.cs diff --git a/libs/nixio/axTLS/bindings/csharp/axTLS.cs b/libs/nixio/axTLS/bindings/csharp/axTLS.cs new file mode 100644 index 000000000..cf64a256e --- /dev/null +++ b/libs/nixio/axTLS/bindings/csharp/axTLS.cs @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * A wrapper around the unmanaged interface to give a semi-decent C# API + */ + +using System; +using System.Runtime.InteropServices; +using System.Net.Sockets; + +/** + * @defgroup csharp_api C# API. + * + * Ensure that the appropriate Dispose() methods are called when finished with + * various objects - otherwise memory leaks will result. + * @{ + */ +namespace axTLS +{ + /** + * @class SSL + * @ingroup csharp_api + * @brief A representation of an SSL connection. + */ + public class SSL + { + public IntPtr m_ssl; /**< A pointer to the real SSL type */ + + /** + * @brief Store the reference to an SSL context. + * @param ip [in] A reference to an SSL object. + */ + public SSL(IntPtr ip) + { + m_ssl = ip; + } + + /** + * @brief Free any used resources on this connection. + * + * A "Close Notify" message is sent on this connection (if possible). + * It is up to the application to close the socket. + */ + public void Dispose() + { + axtls.ssl_free(m_ssl); + } + + /** + * @brief Return the result of a handshake. + * @return SSL_OK if the handshake is complete and ok. + * @see ssl.h for the error code list. + */ + public int HandshakeStatus() + { + return axtls.ssl_handshake_status(m_ssl); + } + + /** + * @brief Return the SSL cipher id. + * @return The cipher id which is one of: + * - SSL_AES128_SHA (0x2f) + * - SSL_AES256_SHA (0x35) + * - SSL_RC4_128_SHA (0x05) + * - SSL_RC4_128_MD5 (0x04) + */ + public byte GetCipherId() + { + return axtls.ssl_get_cipher_id(m_ssl); + } + + /** + * @brief Get the session id for a handshake. + * + * This will be a 32 byte sequence and is available after the first + * handshaking messages are sent. + * @return The session id as a 32 byte sequence. + * @note A SSLv23 handshake may have only 16 valid bytes. + */ + public byte[] GetSessionId() + { + IntPtr ptr = axtls.ssl_get_session_id(m_ssl); + byte sess_id_size = axtls.ssl_get_session_id_size(m_ssl); + byte[] result = new byte[sess_id_size]; + Marshal.Copy(ptr, result, 0, sess_id_size); + return result; + } + + /** + * @brief Retrieve an X.509 distinguished name component. + * + * When a handshake is complete and a certificate has been exchanged, + * then the details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's + * common name matches the URL. + * + * A full handshake needs to occur for this call to work. + * + * @param component [in] one of: + * - SSL_X509_CERT_COMMON_NAME + * - SSL_X509_CERT_ORGANIZATION + * - SSL_X509_CERT_ORGANIZATIONAL_NAME + * - SSL_X509_CA_CERT_COMMON_NAME + * - SSL_X509_CA_CERT_ORGANIZATION + * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + * @return The appropriate string (or null if not defined) + */ + public string GetCertificateDN(int component) + { + return axtls.ssl_get_cert_dn(m_ssl, component); + } + } + + /** + * @class SSLUtil + * @ingroup csharp_api + * @brief Some global helper functions. + */ + public class SSLUtil + { + + /** + * @brief Return the build mode of the axTLS project. + * @return The build mode is one of: + * - SSL_BUILD_SERVER_ONLY + * - SSL_BUILD_ENABLE_VERIFICATION + * - SSL_BUILD_ENABLE_CLIENT + * - SSL_BUILD_FULL_MODE + */ + public static int BuildMode() + { + return axtls.ssl_get_config(axtls.SSL_BUILD_MODE); + } + + /** + * @brief Return the number of chained certificates that the + * client/server supports. + * @return The number of supported server certificates. + */ + public static int MaxCerts() + { + return axtls.ssl_get_config(axtls.SSL_MAX_CERT_CFG_OFFSET); + } + + /** + * @brief Return the number of CA certificates that the client/server + * supports. + * @return The number of supported CA certificates. + */ + public static int MaxCACerts() + { + return axtls.ssl_get_config(axtls.SSL_MAX_CA_CERT_CFG_OFFSET); + } + + /** + * @brief Indicate if PEM is supported. + * @return true if PEM supported. + */ + public static bool HasPEM() + { + return axtls.ssl_get_config(axtls.SSL_HAS_PEM) > 0 ? true : false; + } + + /** + * @brief Display the text string of the error. + * @param error_code [in] The integer error code. + */ + public static void DisplayError(int error_code) + { + axtls.ssl_display_error(error_code); + } + + /** + * @brief Return the version of the axTLS project. + */ + public static string Version() + { + return axtls.ssl_version(); + } + } + + /** + * @class SSLCTX + * @ingroup csharp_api + * @brief A base object for SSLServer/SSLClient. + */ + public class SSLCTX + { + /** + * @brief A reference to the real client/server context. + */ + protected IntPtr m_ctx; + + /** + * @brief Establish a new client/server context. + * + * This function is called before any client/server SSL connections are + * made. If multiple threads are used, then each thread will have its + * own SSLCTX context. Any number of connections may be made with a + * single context. + * + * Each new connection will use the this context's private key and + * certificate chain. If a different certificate chain is required, + * then a different context needs to be be used. + * + * @param options [in] Any particular options. At present the options + * supported are: + * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if + * the server authentication fails. The certificate can be + * authenticated later with a call to VerifyCert(). + * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client + * authentication i.e. each handshake will include a "certificate + * request" message from the server. + * - SSL_DISPLAY_BYTES (full mode build only): Display the byte + * sequences during the handshake. + * - SSL_DISPLAY_STATES (full mode build only): Display the state + * changes during the handshake. + * - SSL_DISPLAY_CERTS (full mode build only): Display the + * certificates that are passed during a handshake. + * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key + * details that are passed during a handshake. + * @param num_sessions [in] The number of sessions to be used for + * session caching. If this value is 0, then there is no session + * caching. + * @return A client/server context. + */ + protected SSLCTX(uint options, int num_sessions) + { + m_ctx = axtls.ssl_ctx_new(options, num_sessions); + } + + /** + * @brief Remove a client/server context. + * + * Frees any used resources used by this context. Each connection will + * be sent a "Close Notify" alert (if possible). + */ + public void Dispose() + { + axtls.ssl_ctx_free(m_ctx); + } + + /** + * @brief Read the SSL data stream. + * @param ssl [in] An SSL object reference. + * @param in_data [out] After a successful read, the decrypted data + * will be here. It will be null otherwise. + * @return The number of decrypted bytes: + * - if > 0, then the handshaking is complete and we are returning the + * number of decrypted bytes. + * - SSL_OK if the handshaking stage is successful (but not yet + * complete). + * - < 0 if an error. + * @see ssl.h for the error code list. + * @note Use in_data before doing any successive ssl calls. + */ + public int Read(SSL ssl, out byte[] in_data) + { + IntPtr ptr = IntPtr.Zero; + int ret = axtls.ssl_read(ssl.m_ssl, ref ptr); + + if (ret > axtls.SSL_OK) + { + in_data = new byte[ret]; + Marshal.Copy(ptr, in_data, 0, ret); + } + else + { + in_data = null; + } + + return ret; + } + + /** + * @brief Write to the SSL data stream. + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ + public int Write(SSL ssl, byte[] out_data) + { + return axtls.ssl_write(ssl.m_ssl, out_data, out_data.Length); + } + + /** + * @brief Write to the SSL data stream. + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @param out_len [in] The number of bytes to be written + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ + public int Write(SSL ssl, byte[] out_data, int out_len) + { + return axtls.ssl_write(ssl.m_ssl, out_data, out_len); + } + + /** + * @brief Find an ssl object based on a Socket reference. + * + * Goes through the list of SSL objects maintained in a client/server + * context to look for a socket match. + * @param s [in] A reference to a Socket object. + * @return A reference to the SSL object. Returns null if the object + * could not be found. + */ + public SSL Find(Socket s) + { + int client_fd = s.Handle.ToInt32(); + return new SSL(axtls. ssl_find(m_ctx, client_fd)); + } + + /** + * @brief Authenticate a received certificate. + * + * This call is usually made by a client after a handshake is complete + * and the context is in SSL_SERVER_VERIFY_LATER mode. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the certificate is verified. + */ + public int VerifyCert(SSL ssl) + { + return axtls.ssl_verify_cert(ssl.m_ssl); + } + + /** + * @brief Force the client to perform its handshake again. + * + * For a client this involves sending another "client hello" message. + * For the server is means sending a "hello request" message. + * + * This is a blocking call on the client (until the handshake + * completes). + * @param ssl [in] An SSL object reference. + * @return SSL_OK if renegotiation instantiation was ok + */ + public int Renegotiate(SSL ssl) + { + return axtls.ssl_renegotiate(ssl.m_ssl); + } + + /** + * @brief Load a file into memory that is in binary DER or ASCII PEM + * format. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param obj_type [in] The format of the file. Can be one of: + * - SSL_OBJ_X509_CERT (no password required) + * - SSL_OBJ_X509_CACERT (no password required) + * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + * - SSL_OBJ_P8 (RC4-128 encrypted data supported) + * - SSL_OBJ_P12 (RC4-128 encrypted data supported) + * + * PEM files are automatically detected (if supported). + * @param filename [in] The location of a file in DER/PEM format. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + */ + public int ObjLoad(int obj_type, string filename, string password) + { + return axtls.ssl_obj_load(m_ctx, obj_type, filename, password); + } + + /** + * @brief Transfer binary data into the object loader. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param obj_type [in] The format of the memory data. + * @param data [in] The binary data to be loaded. + * @param len [in] The amount of data to be loaded. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + */ + public int ObjLoad(int obj_type, byte[] data, int len, string password) + { + return axtls.ssl_obj_memory_load(m_ctx, obj_type, + data, len, password); + } + } + + /** + * @class SSLServer + * @ingroup csharp_api + * @brief The server context. + * + * All server connections are started within a server context. + */ + public class SSLServer : SSLCTX + { + /** + * @brief Start a new server context. + * + * @see SSLCTX for details. + */ + public SSLServer(uint options, int num_sessions) : + base(options, num_sessions) {} + + /** + * @brief Establish a new SSL connection to an SSL client. + * + * It is up to the application to establish the initial socket + * connection. + * + * Call Dispose() when the connection is to be removed. + * @param s [in] A reference to a Socket object. + * @return An SSL object reference. + */ + public SSL Connect(Socket s) + { + int client_fd = s.Handle.ToInt32(); + return new SSL(axtls.ssl_server_new(m_ctx, client_fd)); + } + } + + /** + * @class SSLClient + * @ingroup csharp_api + * @brief The client context. + * + * All client connections are started within a client context. + */ + public class SSLClient : SSLCTX + { + /** + * @brief Start a new client context. + * + * @see SSLCTX for details. + */ + public SSLClient(uint options, int num_sessions) : + base(options, num_sessions) {} + + /** + * @brief Establish a new SSL connection to an SSL server. + * + * It is up to the application to establish the initial socket + * connection. + * + * This is a blocking call - it will finish when the handshake is + * complete (or has failed). + * + * Call Dispose() when the connection is to be removed. + * @param s [in] A reference to a Socket object. + * @param session_id [in] A 32 byte session id for session resumption. + * This can be null if no session resumption is not required. + * @return An SSL object reference. Use SSL.handshakeStatus() to check + * if a handshake succeeded. + */ + public SSL Connect(Socket s, byte[] session_id) + { + int client_fd = s.Handle.ToInt32(); + byte sess_id_size = (byte)(session_id != null ? + session_id.Length : 0); + return new SSL(axtls.ssl_client_new(m_ctx, client_fd, session_id, + sess_id_size)); + } + } +} +/** @} */ diff --git a/libs/nixio/axTLS/bindings/generate_SWIG_interface.pl b/libs/nixio/axTLS/bindings/generate_SWIG_interface.pl new file mode 100755 index 000000000..4b2517988 --- /dev/null +++ b/libs/nixio/axTLS/bindings/generate_SWIG_interface.pl @@ -0,0 +1,393 @@ +#!/usr/bin/perl + +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +#=============================================================== +# Transforms function signature into SWIG format +sub transformSignature +{ + foreach $item (@_) + { + $line =~ s/STDCALL //g; + $line =~ s/EXP_FUNC/extern/g; + + # make API Java more 'byte' friendly + $line =~ s/uint32_t/int/g; + $line =~ s/const uint8_t \* /const unsigned char \* /g; + $line =~ s/\(void\)/()/g; + if ($ARGV[0] eq "-java") + { + $line =~ s/.*ssl_read.*//g; + $line =~ s/const uint8_t \*(\w+)/const signed char $1\[\]/g; + $line =~ s/uint8_t/signed char/g; + } + elsif ($ARGV[0] eq "-perl") + { + $line =~ s/const uint8_t \*(\w+)/const unsigned char $1\[\]/g; + $line =~ s/uint8_t/unsigned char/g; + } + else # lua + { + $line =~ s/const uint8_t \*session_id/const unsigned char session_id\[\]/g; + $line =~ s/const uint8_t \*\w+/unsigned char *INPUT/g; + $line =~ s/uint8_t/unsigned char/g; + } + } + + return $line; +} + +# Parse input file +sub parseFile +{ + foreach $line (@_) + { + next if $line =~ /ssl_x509_create/; # ignore for now + + # test for a #define + if (!$skip && $line =~ m/^#define/) + { + $splitDefine = 1 if $line =~ m/\\$/; + print DATA_OUT $line; + + # check line is not split + next if $splitDefine == 1; + } + + # pick up second line of #define statement + if ($splitDefine) + { + print DATA_OUT $line; + + # check line is not split + $splitDefine = ($line =~ m/\\$/); + next; + } + + # test for function declaration + if (!$skip && $line =~ /EXP_FUNC/ && $line !~/\/\*/) + { + $line = transformSignature($line); + $splitFunctionDeclaration = $line !~ /;/; + print DATA_OUT $line; + next; + } + + if ($splitFunctionDeclaration) + { + $line = transformSignature($line); + $splitFunctionDeclaration = $line !~ /;/; + print DATA_OUT $line; + next; + } + } +} + +#=============================================================== + +# Determine which module to build from cammand-line options +use strict; +use Getopt::Std; + +my $module; +my $interfaceFile; +my $data_file; +my $skip; +my $splitLine; +my @raw_data; + +if (not defined $ARGV[0]) +{ + goto ouch; +} + +if ($ARGV[0] eq "-java") +{ + print "Generating Java interface file\n"; + $module = "axtlsj"; + $interfaceFile = "java/axTLSj.i"; +} +elsif ($ARGV[0] eq "-perl") +{ + print "Generating Perl interface file\n"; + $module = "axtlsp"; + $interfaceFile = "perl/axTLSp.i"; +} +elsif ($ARGV[0] eq "-lua") +{ + print "Generating lua interface file\n"; + $module = "axtlsl"; + $interfaceFile = "lua/axTLSl.i"; +} +else +{ +ouch: + die "Usage: $0 [-java | -perl | -lua]\n"; +} + +# Input file required to generate SWIG interface file. +$data_file = "../ssl/ssl.h"; + +# Open input files +open(DATA_IN, $data_file) || die("Could not open file ($data_file)!"); +@raw_data = ; + +# Open output file +open(DATA_OUT, ">$interfaceFile") || die("Cannot Open File"); + +# +# I wish I could say it was easy to generate the Perl/Java/Lua bindings, +# but each had their own set of challenges... :-(. +# +print DATA_OUT << "END"; +%module $module\n + +/* include our own header */ +%inline %{ +#include "ssl.h" +%} + +%include "typemaps.i" +/* Some SWIG magic to make the API a bit more Java friendly */ +#ifdef SWIGJAVA + +%apply long { SSL * }; +%apply long { SSL_CTX * }; +%apply long { SSLObjLoader * }; + +/* allow "unsigned char []" to become "byte[]" */ +%include "arrays_java.i" + +/* convert these pointers to use long */ +%apply signed char[] {unsigned char *}; +%apply signed char[] {signed char *}; + +/* allow ssl_get_session_id() to return "byte[]" */ +%typemap(out) unsigned char * ssl_get_session_id \"if (result) jresult = SWIG_JavaArrayOutSchar(jenv, result, ssl_get_session_id_size((SSL const *)arg1));\" + +/* allow ssl_client_new() to have a null session_id input */ +%typemap(in) const signed char session_id[] (jbyte *jarr) { + if (jarg3 == NULL) + { + jresult = (jint)ssl_client_new(arg1,arg2,NULL,0); + return jresult; + } + + if (!SWIG_JavaArrayInSchar(jenv, &jarr, &arg3, jarg3)) return 0; +} + +/* Lot's of work required for an ssl_read() due to its various custom + * requirements. + */ +%native (ssl_read) int ssl_read(SSL *ssl, jobject in_data); +%{ +JNIEXPORT jint JNICALL Java_axTLSj_axtlsjJNI_ssl_1read(JNIEnv *jenv, jclass jcls, jint jarg1, jobject jarg2) { + jint jresult = 0 ; + SSL *arg1; + unsigned char *arg2; + jbyte *jarr; + int result; + JNIEnv e = *jenv; + jclass holder_class; + jfieldID fid; + + arg1 = (SSL *)jarg1; + result = (int)ssl_read(arg1, &arg2); + + /* find the "m_buf" entry in the SSLReadHolder class */ + if (!(holder_class = e->GetObjectClass(jenv,jarg2)) || + !(fid = e->GetFieldID(jenv,holder_class, "m_buf", "[B"))) + return SSL_NOT_OK; + + if (result > SSL_OK) + { + int i; + + /* create a new byte array to hold the read data */ + jbyteArray jarray = e->NewByteArray(jenv, result); + + /* copy the bytes across to the java byte array */ + jarr = e->GetByteArrayElements(jenv, jarray, 0); + for (i = 0; i < result; i++) + jarr[i] = (jbyte)arg2[i]; + + /* clean up and set the new m_buf object */ + e->ReleaseByteArrayElements(jenv, jarray, jarr, 0); + e->SetObjectField(jenv, jarg2, fid, jarray); + } + else /* set to null */ + e->SetObjectField(jenv, jarg2, fid, NULL); + + jresult = (jint)result; + return jresult; +} +%} + +/* Big hack to get hold of a socket's file descriptor */ +%typemap (jtype) long "Object" +%typemap (jstype) long "Object" +%native (getFd) int getFd(long sock); +%{ +JNIEXPORT jint JNICALL Java_axTLSj_axtlsjJNI_getFd(JNIEnv *env, jclass jcls, jobject sock) +{ + JNIEnv e = *env; + jfieldID fid; + jobject impl; + jobject fdesc; + + /* get the SocketImpl from the Socket */ + if (!(jcls = e->GetObjectClass(env,sock)) || + !(fid = e->GetFieldID(env,jcls,"impl","Ljava/net/SocketImpl;")) || + !(impl = e->GetObjectField(env,sock,fid))) return -1; + + /* get the FileDescriptor from the SocketImpl */ + if (!(jcls = e->GetObjectClass(env,impl)) || + !(fid = e->GetFieldID(env,jcls,"fd","Ljava/io/FileDescriptor;")) || + !(fdesc = e->GetObjectField(env,impl,fid))) return -1; + + /* get the fd from the FileDescriptor */ + if (!(jcls = e->GetObjectClass(env,fdesc)) || + !(fid = e->GetFieldID(env,jcls,"fd","I"))) return -1; + + /* return the descriptor */ + return e->GetIntField(env,fdesc,fid); +} +%} + +#endif + +/* Some SWIG magic to make the API a bit more Perl friendly */ +#ifdef SWIGPERL + +/* for ssl_session_id() */ +%typemap(out) const unsigned char * { + SV *svs = newSVpv((unsigned char *)\$1, ssl_get_session_id_size((SSL const *)arg1)); + \$result = newRV(svs); + sv_2mortal(\$result); + argvi++; +} + +/* for ssl_write() */ +%typemap(in) const unsigned char out_data[] { + SV* tempsv; + if (!SvROK(\$input)) + croak("Argument \$argnum is not a reference."); + tempsv = SvRV(\$input); + if (SvTYPE(tempsv) != SVt_PV) + croak("Argument \$argnum is not an string."); + \$1 = (unsigned char *)SvPV(tempsv, PL_na); +} + +/* for ssl_read() */ +%typemap(in) unsigned char **in_data (unsigned char *buf) { + \$1 = &buf; +} + +%typemap(argout) unsigned char **in_data { + if (result > SSL_OK) { + SV *svs = newSVpv(*\$1, result); + \$result = newRV(svs); + sv_2mortal(\$result); + argvi++; + } +} + +/* for ssl_client_new() */ +%typemap(in) const unsigned char session_id[] { + /* check for a reference */ + if (SvOK(\$input) && SvROK(\$input)) { + SV* tempsv = SvRV(\$input); + if (SvTYPE(tempsv) != SVt_PV) + croak("Argument \$argnum is not an string."); + \$1 = (unsigned char *)SvPV(tempsv, PL_na); + } + else + \$1 = NULL; +} + +#endif + +/* Some SWIG magic to make the API a bit more Lua friendly */ +#ifdef SWIGLUA +SWIG_NUMBER_TYPEMAP(unsigned char); +SWIG_TYPEMAP_NUM_ARR(uchar,unsigned char); + +/* for ssl_session_id() */ +%typemap(out) const unsigned char * { + int i; + lua_newtable(L); + for (i = 0; i < ssl_get_session_id_size((SSL const *)arg1); i++){ + lua_pushnumber(L,(lua_Number)result[i]); + lua_rawseti(L,-2,i+1); /* -1 is the number, -2 is the table */ + } + SWIG_arg++; +} + +/* for ssl_read() */ +%typemap(in) unsigned char **in_data (unsigned char *buf) { + \$1 = &buf; +} + +%typemap(argout) unsigned char **in_data { + if (result > SSL_OK) { + int i; + lua_newtable(L); + for (i = 0; i < result; i++){ + lua_pushnumber(L,(lua_Number)buf2[i]); + lua_rawseti(L,-2,i+1); /* -1 is the number, -2 is the table */ + } + SWIG_arg++; + } +} + +/* for ssl_client_new() */ +%typemap(in) const unsigned char session_id[] { + if (lua_isnil(L,\$input)) + \$1 = NULL; + else + \$1 = SWIG_get_uchar_num_array_fixed(L,\$input, ssl_get_session_id((SSL const *)\$1)); +} + +#endif + +END + +# Initialise loop variables +$skip = 1; +$splitLine = 0; + +parseFile(@raw_data); + +close(DATA_IN); +close(DATA_OUT); + +#=============================================================== + diff --git a/libs/nixio/axTLS/bindings/generate_interface.pl b/libs/nixio/axTLS/bindings/generate_interface.pl new file mode 100755 index 000000000..c24bff9f4 --- /dev/null +++ b/libs/nixio/axTLS/bindings/generate_interface.pl @@ -0,0 +1,322 @@ +#!/usr/bin/perl -w + +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +#=============================================================== +# This application transforms ssl.h into interfaces that can be used by +# other language bindings. It is "SWIG"-like in nature in that various +# files are generated based on the axTLS API. +# +# The file produced is axInterface.? (depending on the file extension). +# +#=============================================================== + +use strict; + +my $CSHARP = 0; +my $VBNET = 1; + +my $binding; +my $skip = 0; +my $signature_ret_type; + +# Transforms function signature into an Interface format +sub transformSignature +{ + my $item; + my ($line) = @_; + + foreach $item ($line) + { + # our very basic preprocessor + if ($binding == $CSHARP) + { + $line =~ s/STDCALL //; + $line =~ s/EXP_FUNC/ [DllImport ("axtls")]\n public static extern/; + $line =~ s/uint32_t/uint/g; + $line =~ s/uint8_t \*\*/ref IntPtr /g; + $line =~ s/const uint8_t \* /IntPtr /g; + $line =~ s/const uint8_t \*/byte[] /g; # note: subtle diff + $line =~ s/uint8_t \* ?/byte[] /g; + $line =~ s/uint8_t ?/byte /g; + $line =~ s/const char \* ?/string /g; + $line =~ s/const SSL_CTX \* ?/IntPtr /g; + $line =~ s/SSL_CTX \* ?/IntPtr /g; + $line =~ s/SSLObjLoader \* ?/IntPtr /g; + $line =~ s/const SSL \* ?/IntPtr /g; + $line =~ s/SSL \* ?/IntPtr /g; + $line =~ s/\(void\)/()/g; + } + elsif ($binding == $VBNET) + { + if ($line =~ /EXP_FUNC/) + { + # Procedure or function? + my $invariant = $line =~ /void /; + + my $proc = $invariant ? "Sub" : "Function"; + ($signature_ret_type) = $line =~ /EXP_FUNC (.*) STDCALL/; + $line =~ s/EXP_FUNC .* STDCALL / Public Shared $proc _\n /; + + $signature_ret_type =~ s/const uint8_t \*/As IntPtr/; + $signature_ret_type =~ s/const char \*/As String/; + $signature_ret_type =~ s/SSL_CTX \*/As IntPtr/; + $signature_ret_type =~ s/SSLObjLoader \*/As IntPtr/; + $signature_ret_type =~ s/SSL \*/As IntPtr/; + $signature_ret_type =~ s/uint8_t/As Byte/; + $signature_ret_type =~ s/int/As Integer/; + $signature_ret_type =~ s/void//; + $signature_ret_type .= "\n End $proc\n\n"; + } + + $line =~ s/uint32_t (\w+)/ByVal $1 As Integer/g; + $line =~ s/int (\w+)/ByVal $1 As Integer/g; + $line =~ s/uint8_t \*\* ?(\w+)/ByRef $1 As IntPtr/g; + $line =~ s/const uint8_t \* ?(\w+)/ByVal $1() As Byte/g; + $line =~ s/uint8_t \* ?(\w+)/ByVal $1() As Byte/g; + $line =~ s/uint8_t ?(\w+)/ByVal $1 As Byte/g; + $line =~ s/const char \* ?(\w+)/ByVal $1 As String/g; + $line =~ s/const SSL_CTX \* ?(\w+)/ByVal $1 As IntPtr/g; + $line =~ s/SSL_CTX \* ?(\w+)/ByVal $1 As IntPtr/g; + $line =~ s/SSLObjLoader \* ?(\w+)/ByVal $1 As IntPtr/g; + $line =~ s/const SSL \* ?(\w+)/ByVal $1 As IntPtr/g; + $line =~ s/SSL \* ?(\w+)/ByVal $1 As IntPtr/g; + $line =~ s/void \* ?(\w+)/Byval $1 As IntPtr/g; + $line =~ s/\(void\)/()/g; + $line =~ s/void//g; + $line =~ s/;\n/ $signature_ret_type;/; + } + } + + return $line; +} + +# Parse input file +sub parseFile +{ + my (@file) = @_; + my $line; + my $splitDefine = 0; + my $splitFunctionDeclaration; + my $vb_hack = " "; + my $vb_line_hack = 0; + + $skip = 0; + + foreach $line (@file) + { + next if $line =~ /sl_x509_create/; # ignore for now + + # test for a #define + if (!$skip && $line =~ m/^#define/) + { + $splitDefine = 1 if $line =~ m/\\$/; + + if ($binding == $VBNET) + { + $line =~ s/\|/Or/g; + $line =~ s/ 0x/ &H/; + } + + my ($name, $value) = $line =~ /#define (\w+) +([^\\]*)[\\]?\n/; + + if (defined $name && defined $value) + { + # C# constant translation + if ($binding == $CSHARP) + { + $line = " public const int $name = $value"; + } + # VB.NET constant translation + elsif ($binding == $VBNET) + { + $line = " Public Const $name As Integer = $value"; + } + } + + next if $line =~ /#define/; # ignore any other defines + + print DATA_OUT $line; + + # check line is not split + next if $splitDefine == 1; + print DATA_OUT ";" if $binding == $CSHARP; + print DATA_OUT "\n"; + } + + # pick up second line of #define statement + if ($splitDefine) + { + if ($line !~ /\\$/) + { + $line =~ s/$/;/ if $binding == $CSHARP; # add the ";" + } + + $line =~ s/ ?\| ?/ Or /g + if ($binding == $VBNET); + + # check line is not split + $splitDefine = ($line =~ m/\\$/); + + # ignore trailing "\" + $line =~ s/\\$// if $binding == $CSHARP; + $line =~ s/\\$/_/ if $binding == $VBNET; + print DATA_OUT $line; + next; + } + + # test for function declaration + if (!$skip && $line =~ /EXP_FUNC/ && $line !~ /\/\*/) + { + $line = transformSignature($line); + $splitFunctionDeclaration = $line !~ /;/; + $line =~ s/;// if ($binding == $VBNET); + $line =~ s/\n$/ _\n/ if ($binding == $VBNET) && + $splitFunctionDeclaration; + print DATA_OUT $line; + next; + } + + if ($splitFunctionDeclaration) + { + $line = transformSignature($line); + $splitFunctionDeclaration = $line !~ /;/; + $line =~ s/;// if ($binding == $VBNET); + $line =~ s/\n/ _\n/ if ($binding == $VBNET) && + $splitFunctionDeclaration == 1; + print DATA_OUT $line; + next; + } + } +} + +#=============================================================== + +# Determine which module to build from command-line options +use strict; +use Getopt::Std; + +my $binding_prefix; +my $binding_suffix; +my $data_file; +my @raw_data; + +if (not defined $ARGV[0]) +{ + goto ouch; +} + +if ($ARGV[0] eq "-csharp") +{ + print "Generating C# interface file\n"; + $binding_prefix = "csharp"; + $binding_suffix = "cs"; + $binding = $CSHARP; +} +elsif ($ARGV[0] eq "-vbnet") +{ + print "Generating VB.NET interface file\n"; + $binding_prefix = "vbnet"; + $binding_suffix = "vb"; + $binding = $VBNET; +} +else +{ +ouch: + die "Usage: $0 [-csharp | -vbnet]\n"; +} + +my $interfaceFile = "$binding_prefix/axInterface.$binding_suffix"; + +# Input file required to generate interface file. +$data_file = "../ssl/ssl.h"; + +# Open input files +open(DATA_IN, $data_file) || die("Could not open file ($data_file)!"); +@raw_data = ; + + +# Open output file +if ($binding == $CSHARP || $binding == $VBNET) +{ + open(DATA_OUT, ">$interfaceFile") || die("Cannot Open File"); +} + +# SPEC interface file header +if ($binding == $CSHARP) +{ + # generate the C#/C interface file + print DATA_OUT << "END"; +// The C# to C interface definition file for the axTLS project +// Do not modify - this file is generated + +using System; +using System.Runtime.InteropServices; + +namespace axTLS +{ + public class axtls + { +END +} +elsif ($binding == $VBNET) +{ + # generate the VB.NET/C interface file + print DATA_OUT << "END"; +' The VB.NET to C interface definition file for the axTLS project +' Do not modify - this file is generated + +Imports System +Imports System.Runtime.InteropServices + +Namespace axTLSvb + Public Class axtls +END +} + +parseFile(@raw_data); + +# finish up +if ($binding == $CSHARP) +{ + print DATA_OUT " };\n"; + print DATA_OUT "};\n"; +} +elsif ($binding == $VBNET) +{ + print DATA_OUT " End Class\nEnd Namespace\n"; +} + +close(DATA_IN); +close(DATA_OUT); + +#=============================================================== + diff --git a/libs/nixio/axTLS/bindings/java/Makefile b/libs/nixio/axTLS/bindings/java/Makefile new file mode 100644 index 000000000..8df1d0aa8 --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/Makefile @@ -0,0 +1,94 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +AXTLS_HOME=../.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf +include $(AXTLS_HOME)/config/makefile.java.conf + +all: lib jar + +JAR=$(AXTLS_HOME)/$(STAGE)/axtls.jar + +ifdef CONFIG_PLATFORM_WIN32 +TARGET=$(AXTLS_HOME)/$(STAGE)/axtlsj.dll +else +TARGET=$(AXTLS_HOME)/$(STAGE)/libaxtlsj.so +endif + +lib: $(TARGET) +axTLSj_wrap.o : axTLSj_wrap.c + +JAVA_FILES= \ + axtlsjJNI.java \ + axtlsjConstants.java \ + axtlsj.java \ + SSLReadHolder.java \ + SSL.java \ + SSLUtil.java \ + SSLCTX.java \ + SSLServer.java \ + SSLClient.java + +OBJ=axTLSj_wrap.o + +JAVA_CLASSES:=$(JAVA_FILES:%.java=classes/axTLSj/%.class) + +ifdef CONFIG_PLATFORM_WIN32 +LDFLAGS += axtls.lib /libpath:"$(AXTLS_HOME)/$(STAGE)" + +include $(AXTLS_HOME)/config/makefile.post + +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) $(LDSHARED) /out:$@ $(OBJ) +else # Not Win32 + +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) -L $(AXTLS_HOME)/$(STAGE) $(LDSHARED) -o $@ $(OBJ) -laxtls +endif + +jar: $(OBJ) $(JAR) + +# if we are doing the samples then defer creating the jar until then +$(JAR): $(JAVA_CLASSES) +ifndef CONFIG_JAVA_SAMPLES + jar cvf $@ -C classes axTLSj +else + @if [ ! -f $(JAR) ]; then touch $(JAR); fi +endif + +classes/axTLSj/%.class : %.java + javac -d classes -classpath classes $^ + +clean:: + @rm -f $(JAR) $(TARGET) SWIG* axtls* *.i *.c + @rm -fr classes/* + diff --git a/libs/nixio/axTLS/bindings/java/SSL.java b/libs/nixio/axTLS/bindings/java/SSL.java new file mode 100644 index 000000000..b53a6da06 --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSL.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +import java.io.*; +import java.util.*; + +/** + * @defgroup java_api Java API. + * + * Ensure that the appropriate dispose() methods are called when finished with + * various objects - otherwise memory leaks will result. + */ + +/** + * @class SSL + * @ingroup java_api + * @brief A representation of an SSL connection. + * + */ +public class SSL +{ + public int m_ssl; /**< A pointer to the real SSL type */ + + /** + * @brief Store the reference to an SSL context. + * @param ip [in] A reference to an SSL object. + */ + public SSL(int ip) + { + m_ssl = ip; + } + + /** + * @brief Free any used resources on this connection. + * + * A "Close Notify" message is sent on this connection (if possible). It + * is up to the application to close the socket. + */ + public void dispose() + { + axtlsj.ssl_free(m_ssl); + } + + /** + * @brief Return the result of a handshake. + * @return SSL_OK if the handshake is complete and ok. + * @see ssl.h for the error code list. + */ + public int handshakeStatus() + { + return axtlsj.ssl_handshake_status(m_ssl); + } + + /** + * @brief Return the SSL cipher id. + * @return The cipher id which is one of: + * - SSL_AES128_SHA (0x2f) + * - SSL_AES256_SHA (0x35) + * - SSL_RC4_128_SHA (0x05) + * - SSL_RC4_128_MD5 (0x04) + */ + public byte getCipherId() + { + return axtlsj.ssl_get_cipher_id(m_ssl); + } + + /** + * @brief Get the session id for a handshake. + * + * This will be a 32 byte sequence and is available after the first + * handshaking messages are sent. + * @return The session id as a 32 byte sequence. + * @note A SSLv23 handshake may have only 16 valid bytes. + */ + public byte[] getSessionId() + { + return axtlsj.ssl_get_session_id(m_ssl); + } + + /** + * @brief Retrieve an X.509 distinguished name component. + * + * When a handshake is complete and a certificate has been exchanged, + * then the details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's common + * name matches the URL. + * + * A full handshake needs to occur for this call to work. + * + * @param component [in] one of: + * - SSL_X509_CERT_COMMON_NAME + * - SSL_X509_CERT_ORGANIZATION + * - SSL_X509_CERT_ORGANIZATIONAL_NAME + * - SSL_X509_CA_CERT_COMMON_NAME + * - SSL_X509_CA_CERT_ORGANIZATION + * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + * @return The appropriate string (or null if not defined) + */ + public String getCertificateDN(int component) + { + return axtlsj.ssl_get_cert_dn(m_ssl, component); + } +} diff --git a/libs/nixio/axTLS/bindings/java/SSLCTX.java b/libs/nixio/axTLS/bindings/java/SSLCTX.java new file mode 100644 index 000000000..1cd3e032f --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSLCTX.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +import java.net.*; + +/** + * @class SSLCTX + * @ingroup java_api + * @brief A base object for SSLServer/SSLClient. + */ +public class SSLCTX +{ + /** + * A reference to the real client/server context. + */ + protected int m_ctx; + + /** + * @brief Establish a new client/server context. + * + * This function is called before any client/server SSL connections are + * made. If multiple threads are used, then each thread will have its + * own SSLCTX context. Any number of connections may be made with a single + * context. + * + * Each new connection will use the this context's private key and + * certificate chain. If a different certificate chain is required, then a + * different context needs to be be used. + * + * @param options [in] Any particular options. At present the options + * supported are: + * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the + * server authentication fails. The certificate can be authenticated later + * with a call to verifyCert(). + * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication + * i.e. each handshake will include a "certificate request" message from + * the server. + * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences + * during the handshake. + * - SSL_DISPLAY_STATES (full mode build only): Display the state changes + * during the handshake. + * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that + * are passed during a handshake. + * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details + * that are passed during a handshake. + * + * @param num_sessions [in] The number of sessions to be used for session + * caching. If this value is 0, then there is no session caching. + * + * If this option is null, then the default internal private key/ + * certificate pair is used (if CONFIG_SSL_USE_DEFAULT_KEY is set). + * + * The resources used by this object are automatically freed. + * @return A client/server context. + */ + protected SSLCTX(int options, int num_sessions) + { + m_ctx = axtlsj.ssl_ctx_new(options, num_sessions); + } + + /** + * @brief Remove a client/server context. + * + * Frees any used resources used by this context. Each connection will be + * sent a "Close Notify" alert (if possible). + */ + public void dispose() + { + axtlsj.ssl_ctx_free(m_ctx); + } + + /** + * @brief Read the SSL data stream. + * @param ssl [in] An SSL object reference. + * @param rh [out] After a successful read, the decrypted data can be + * retrieved with rh.getData(). It will be null otherwise. + * @return The number of decrypted bytes: + * - if > 0, then the handshaking is complete and we are returning the + * number of decrypted bytes. + * - SSL_OK if the handshaking stage is successful (but not yet complete). + * - < 0 if an error. + * @see ssl.h for the error code list. + * @note Use rh before doing any successive ssl calls. + */ + public int read(SSL ssl, SSLReadHolder rh) + { + return axtlsj.ssl_read(ssl.m_ssl, rh); + } + + /** + * @brief Write to the SSL data stream. + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ + public int write(SSL ssl, byte[] out_data) + { + return axtlsj.ssl_write(ssl.m_ssl, out_data, out_data.length); + } + + /** + * @brief Write to the SSL data stream. + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @param out_len [in] The number of bytes to be written + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ + public int write(SSL ssl, byte[] out_data, int out_len) + { + return axtlsj.ssl_write(ssl.m_ssl, out_data, out_len); + } + + /** + * @brief Find an ssl object based on a Socket reference. + * + * Goes through the list of SSL objects maintained in a client/server + * context to look for a socket match. + * @param s [in] A reference to a Socket object. + * @return A reference to the SSL object. Returns null if the object + * could not be found. + */ + public SSL find(Socket s) + { + int client_fd = axtlsj.getFd(s); + return new SSL(axtlsj.ssl_find(m_ctx, client_fd)); + } + + /** + * @brief Authenticate a received certificate. + * + * This call is usually made by a client after a handshake is complete + * and the context is in SSL_SERVER_VERIFY_LATER mode. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the certificate is verified. + */ + public int verifyCert(SSL ssl) + { + return axtlsj.ssl_verify_cert(ssl.m_ssl); + } + + /** + * @brief Force the client to perform its handshake again. + * + * For a client this involves sending another "client hello" message. + * For the server is means sending a "hello request" message. + * + * This is a blocking call on the client (until the handshake completes). + * @param ssl [in] An SSL object reference. + * @return SSL_OK if renegotiation instantiation was ok + */ + public int renegotiate(SSL ssl) + { + return axtlsj.ssl_renegotiate(ssl.m_ssl); + } + + /** + * @brief Load a file into memory that is in binary DER or ASCII PEM format. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param obj_type [in] The format of the file. Can be one of: + * - SSL_OBJ_X509_CERT (no password required) + * - SSL_OBJ_X509_CACERT (no password required) + * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + * - SSL_OBJ_P8 (RC4-128 encrypted data supported) + * - SSL_OBJ_P12 (RC4-128 encrypted data supported) + * + * PEM files are automatically detected (if supported). + * @param filename [in] The location of a file in DER/PEM format. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + */ + public int objLoad(int obj_type, String filename, String password) + { + return axtlsj.ssl_obj_load(m_ctx, obj_type, filename, password); + } + + /** + * @brief Transfer binary data into the object loader. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param obj_type [in] The format of the memory data. + * @param data [in] The binary data to be loaded. + * @param len [in] The amount of data to be loaded. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + */ + + public int objLoad(int obj_type, byte[] data, int len, String password) + { + return axtlsj.ssl_obj_memory_load(m_ctx, obj_type, data, len, password); + } +} diff --git a/libs/nixio/axTLS/bindings/java/SSLClient.java b/libs/nixio/axTLS/bindings/java/SSLClient.java new file mode 100644 index 000000000..f65fe9c53 --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSLClient.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +import java.net.*; + +/** + * @class SSLClient + * @ingroup java_api + * @brief The client context. + * + * All client connections are started within a client context. + */ +public class SSLClient extends SSLCTX +{ + /** + * @brief Start a new client context. + * + * @see SSLCTX for details. + */ + public SSLClient(int options, int num_sessions) + { + super(options, num_sessions); + } + + /** + * @brief Establish a new SSL connection to an SSL server. + * + * It is up to the application to establish the initial socket connection. + * + * This is a blocking call - it will finish when the handshake is + * complete (or has failed). + * + * Call dispose() when the connection is to be removed. + * @param s [in] A reference to a Socket object. + * @param session_id [in] A 32 byte session id for session resumption. This + * can be null if no session resumption is not required. + * @return An SSL object reference. Use SSL.handshakeStatus() to check + * if a handshake succeeded. + */ + public SSL connect(Socket s, byte[] session_id) + { + int client_fd = axtlsj.getFd(s); + byte sess_id_size = (byte)(session_id != null ? + session_id.length : 0); + return new SSL(axtlsj.ssl_client_new(m_ctx, client_fd, session_id, + sess_id_size)); + } +} diff --git a/libs/nixio/axTLS/bindings/java/SSLReadHolder.java b/libs/nixio/axTLS/bindings/java/SSLReadHolder.java new file mode 100644 index 000000000..91fd76b23 --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSLReadHolder.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +/** + * @class SSLReadHolder + * @ingroup java_api + * @brief A holder for data read in an SSL read. + */ +public class SSLReadHolder +{ + /** + * @brief Contruct a new read holder object. + */ + public SSLReadHolder() + { + m_buf = null; + } + + /** + * @brief Retrieve the reference to the read data. + */ + public byte[] getData() + { + return m_buf; + } + + private byte[] m_buf; +} diff --git a/libs/nixio/axTLS/bindings/java/SSLServer.java b/libs/nixio/axTLS/bindings/java/SSLServer.java new file mode 100644 index 000000000..514ccb034 --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSLServer.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +import java.net.*; + +/** + * @class SSLServer + * @ingroup java_api + * @brief The server context. + * + * All server connections are started within a server context. + */ +public class SSLServer extends SSLCTX +{ + /** + * @brief Start a new server context. + * + * @see SSLCTX for details. + */ + public SSLServer(int options, int num_sessions) + { + super(options, num_sessions); + } + + /** + * @brief Establish a new SSL connection to an SSL client. + * + * It is up to the application to establish the initial socket connection. + * + * Call dispose() when the connection is to be removed. + * @param s [in] A reference to a Socket object. + * @return An SSL object reference. + */ + public SSL connect(Socket s) + { + int client_fd = axtlsj.getFd(s); + return new SSL(axtlsj.ssl_server_new(m_ctx, client_fd)); + } +} diff --git a/libs/nixio/axTLS/bindings/java/SSLUtil.java b/libs/nixio/axTLS/bindings/java/SSLUtil.java new file mode 100644 index 000000000..3d53de51c --- /dev/null +++ b/libs/nixio/axTLS/bindings/java/SSLUtil.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * A wrapper around the unmanaged interface to give a semi-decent Java API + */ + +package axTLSj; + +import java.io.*; +import java.util.*; + +/** + * @class SSLUtil + * @ingroup java_api + * @brief Some global helper functions. + * + */ +public class SSLUtil +{ + /** + * @brief Load up the ddl/shared library + */ + static + { + System.loadLibrary("axtlsj"); + } + + /** + * @brief Return the build mode of the axTLS project. + * @return The build mode is one of: + * - SSL_BUILD_SERVER_ONLY + * - SSL_BUILD_ENABLE_VERIFICATION + * - SSL_BUILD_ENABLE_CLIENT + * - SSL_BUILD_FULL_MODE + */ + public static int buildMode() + { + return axtlsj.ssl_get_config(axtlsj.SSL_BUILD_MODE); + } + + /** + * @brief Return the number of chained certificates that the client/server + * supports. + * @return The number of supported client/server certificates. + */ + public static int maxCerts() + { + return axtlsj.ssl_get_config(axtlsj.SSL_MAX_CERT_CFG_OFFSET); + } + + /** + * @brief Return the number of CA certificates that the client/server + * supports. + * @return The number of supported CA certificates. + */ + public static int maxCACerts() + { + return axtlsj.ssl_get_config(axtlsj.SSL_MAX_CA_CERT_CFG_OFFSET); + } + + /** + * @brief Indicate if PEM is supported. + * @return true if PEM supported. + */ + public static boolean hasPEM() + { + return axtlsj.ssl_get_config(axtlsj.SSL_HAS_PEM) > 0 ? true : false; + } + + /** + * @brief Display the text string of the error. + * @param error_code [in] The integer error code. + * @see ssl.h for the error code list. + */ + public static void displayError(int error_code) + { + axtlsj.ssl_display_error(error_code); + } + + /** + * @brief Return the version of the axTLS project. + */ + public static String version() + { + return axtlsj.ssl_version(); + } +} + diff --git a/libs/nixio/axTLS/bindings/lua/Makefile b/libs/nixio/axTLS/bindings/lua/Makefile new file mode 100644 index 000000000..daacf9215 --- /dev/null +++ b/libs/nixio/axTLS/bindings/lua/Makefile @@ -0,0 +1,67 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +AXTLS_HOME=../.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +all: lib + + +ifdef CONFIG_PLATFORM_WIN32 +TARGET=$(AXTLS_HOME)/$(STAGE)/axtlsl.dll +else +TARGET=$(CONFIG_LUA_CORE)/lib/lua/5.1/axtlsl.so +endif + +ifneq ($(MAKECMDGOALS), clean) + +lib: $(TARGET) +OBJ:=axTLSl_wrap.o +include $(AXTLS_HOME)/config/makefile.post + +# there are a few static functions that aren't used +CFLAGS += -funit-at-a-time + +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) $(LDSHARED) -o $@ $^ -L$(AXTLS_HOME)/$(STAGE) -L$(CONFIG_LUA_CORE)/lib -laxtls -llua + +CFLAGS += -I $(CONFIG_LUA_CORE)/include +else +CFLAGS += /I"`cygpath -w $(CONFIG_LUA_CORE)/include`" +LDFLAGS += axtls.lib /libpath:"$(AXTLS_HOME)/$(STAGE)" + +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) $(LDSHARED) /out:$@ $(OBJ) +endif # WIN32 + +clean:: + @rm -f $(TARGET) *.i axTLSl* .depend diff --git a/libs/nixio/axTLS/bindings/perl/Makefile b/libs/nixio/axTLS/bindings/perl/Makefile new file mode 100644 index 000000000..92fd3c50f --- /dev/null +++ b/libs/nixio/axTLS/bindings/perl/Makefile @@ -0,0 +1,91 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +AXTLS_HOME=../.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +all: lib + +ifdef CONFIG_PLATFORM_WIN32 +TARGET=$(AXTLS_HOME)/$(STAGE)/axtlsp.dll +else +TARGET=$(AXTLS_HOME)/$(STAGE)/libaxtlsp.so +endif + +ifneq ($(MAKECMDGOALS), clean) + +ifdef CONFIG_PLATFORM_WIN32 +PERL5_CORE:=$(shell cygpath -w "$(CONFIG_PERL_CORE)") +else +PERL5_CORE= $(shell perl -e 'use Config; print $$Config{archlib};')/CORE +endif + +all: test_perl + +test_perl: + @if ! [ -d "$(PERL5_CORE)" ]; then \ + echo "*** Error: Perl not installed at $(CONFIG_PERL_CORE) - go to " \ + "http://www.cpan.org/authors/id/G/GR/GRAHAMC/SiePerl-5.8.0-bin-1.0-Win32.INSTALL.exe" && exit 1; \ + fi + +endif + +lib: $(TARGET) +OBJ:=axTLSp_wrap.o +include $(AXTLS_HOME)/config/makefile.post + +ifndef CONFIG_PLATFORM_WIN32 # Linux/Unix/Cygwin + +# +# Could have used libperl.a, but it increases the library to over 1MB, so just +# use libperl.so. But this needs to be in the shared library path for things to +# work. +# +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) -L$(AXTLS_HOME)/$(STAGE) -L$(PERL5_CORE) $(LDSHARED) -o $@ $(OBJ) -laxtls -lperl +ifdef CONFIG_PLATFORM_CYGWIN + cd $(AXTLS_HOME)/$(STAGE); ln -sf $(notdir $@) axtlsp.dll +endif + @install axtlsp.pm $(AXTLS_HOME)/$(STAGE) + +CFLAGS += -D_GNU_SOURCE -I$(PERL5_CORE) +else +CFLAGS += /I"$(PERL5_CORE)" +LDFLAGS += $(CONFIG_PERL_LIB) /libpath:"$(PERL5_CORE)" axtls.lib /libpath:"$(AXTLS_HOME)/$(STAGE)" + +$(TARGET) : $(OBJ) + $(LD) $(LDFLAGS) $(LDSHARED) /out:$@ $(OBJ) + install axtlsp.pm $(AXTLS_HOME)/$(STAGE) +endif # WIN32 + +clean:: + @rm -f $(TARGET) axtls* *.i axTLSp* *.c .depend $(AXTLS_HOME)/$(STAGE)/axtlsp.pm diff --git a/libs/nixio/axTLS/bindings/vbnet/Makefile b/libs/nixio/axTLS/bindings/vbnet/Makefile new file mode 100644 index 000000000..7da60d02e --- /dev/null +++ b/libs/nixio/axTLS/bindings/vbnet/Makefile @@ -0,0 +1,35 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf + +clean:: + @rm -f axssl* axInterface.vb diff --git a/libs/nixio/axTLS/bindings/vbnet/axTLSvb.vb b/libs/nixio/axTLS/bindings/vbnet/axTLSvb.vb new file mode 100644 index 000000000..9388273ce --- /dev/null +++ b/libs/nixio/axTLS/bindings/vbnet/axTLSvb.vb @@ -0,0 +1,200 @@ +' +' Copyright (c) 2007, Cameron Rich +' +' All rights reserved. +' +' Redistribution and use in source and binary forms, with or without +' modification, are permitted provided that the following conditions are met: +' +' * Redistributions of source code must retain the above copyright notice, +' this list of conditions and the following disclaimer. +' * 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. +' * Neither the name of the axTLS project nor the names of its +' contributors may be used to endorse or promote products derived +' from this software without specific prior written permission. +' +' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +' "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 THE COPYRIGHT OWNER 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. +' + +' +' A wrapper around the unmanaged Integererface to give a semi-decent VB.NET API +' + +Imports System +Imports System.Runtime.InteropServices +Imports System.Net.Sockets +Imports axTLSvb + +Namespace axTLSvb + Public Class SSL + Public m_ssl As IntPtr + + Public Sub New(ByRef ip As IntPtr) + m_ssl = ip + End Sub + + Public Sub Dispose() + axtls.ssl_free(m_ssl) + End Sub + + Public Function HandshakeStatus() As Integer + Return axtls.ssl_handshake_status(m_ssl) + End Function + + Public Function GetCipherId() As Byte + Return axtls.ssl_get_cipher_id(m_ssl) + End Function + + Public Function GetSessionId() As Byte() + Dim ptr As IntPtr = axtls.ssl_get_session_id(m_ssl) + Dim sess_id_size As Integer = axtls.ssl_get_session_id_size(m_ssl) + Dim result(sess_id_size-1) As Byte + Marshal.Copy(ptr, result, 0, sess_id_size) + Return result + End Function + + Public Function GetCertificateDN(component As Integer) As String + Return axtls.ssl_get_cert_dn(m_ssl, component) + End Function + End Class + + Public Class SSLUtil + Private dummy As Integer ' need something here + + Public Shared Function BuildMode() As Integer + Return axtls.ssl_get_config(axtls.SSL_BUILD_MODE) + End Function + + Public Shared Function MaxCerts() As Integer + Return axtls.ssl_get_config(axtls.SSL_MAX_CERT_CFG_OFFSET) + End Function + + Public Shared Function MaxCACerts() As Integer + Return axtls.ssl_get_config(axtls.SSL_MAX_CA_CERT_CFG_OFFSET) + End Function + + Public Shared Function HasPEM() As Boolean + If axtls.ssl_get_config(axtls.SSL_HAS_PEM) > 0 Then + Return True + Else + Return False + End If + End Function + + Public Shared Sub DisplayError(ByVal error_code As Integer) + axtls.ssl_display_error(error_code) + End Sub + + Public Shared Function Version() As String + Return axtls.ssl_version() + End Function + End Class + + Public Class SSLCTX + Protected m_ctx As IntPtr + + Protected Sub New(ByVal options As Integer, _ + ByVal num_sessions As Integer) + m_ctx = axtls.ssl_ctx_new(options, num_sessions) + End Sub + + Public Sub Dispose() + axtls.ssl_ctx_free(m_ctx) + End Sub + + Public Function Read(ByVal ssl As SSL, ByRef in_data As Byte()) As Integer + Dim ptr As IntPtr = IntPtr.Zero + Dim ret as Integer = axtls.ssl_read(ssl.m_ssl, ptr) + + If ret > axtls.SSL_OK Then + ReDim in_data(ret) + Marshal.Copy(ptr, in_data, 0, ret) + Else + in_data = Nothing + End If + + Return ret + End Function + + Public Function Write(ByVal ssl As SSL, _ + ByVal data As Byte(), len As Integer) As Integer + Return axtls.ssl_write(ssl.m_ssl, data, len) + End Function + + Public Function Find(ByVal s As Socket) As SSL + Dim client_fd As Integer = s.Handle.ToInt32() + Return New SSL(axtls.ssl_find(m_ctx, client_fd)) + End Function + + Public Function VerifyCert(ByVal ssl As SSL) As Integer + Return axtls.ssl_verify_cert(ssl.m_ssl) + End Function + + Public Function Renegotiate(ByVal ssl As SSL) As Integer + Return axtls.ssl_renegotiate(ssl.m_ssl) + End Function + + Public Function ObjLoad(ByVal obj_type As Integer, _ + ByVal filename As String, _ + password As String) As Integer + Return axtls.ssl_obj_load(m_ctx, obj_type, filename, password) + End Function + + Public Function ObjLoad(ByVal obj_type As Integer, _ + ByVal data As Byte(), ByVal len As Integer, _ + password As String) As Integer + Return axtls.ssl_obj_memory_load( _ + m_ctx, obj_type, data, len, password) + End Function + End Class + + Public Class SSLServer + Inherits SSLCTX + + Public Sub New(ByVal options As Integer, _ + ByVal num_sessions As Integer) + MyBase.New(options, num_sessions) + End Sub + + Public Function Connect(ByVal s As Socket) As SSL + Dim client_fd As Integer = s.Handle.ToInt32() + Return New SSL(axtls.ssl_server_new(m_ctx, client_fd)) + End Function + End Class + + Public Class SSLClient + Inherits SSLCTX + + Public Sub New(ByVal options As Integer, _ + ByVal num_sessions As Integer) + MyBase.New(options, num_sessions) + End Sub + + Public Function Connect(ByVal s As Socket, _ + ByVal session_id As Byte()) As SSL + Dim client_fd As Integer = s.Handle.ToInt32() + Dim sess_id_size As Byte + If session_id is Nothing Then + sess_id_size = 0 + Else + sess_id_size = session_id.Length + End If + + Return New SSL(axtls.ssl_client_new(m_ctx, client_fd, session_id, _ + sess_id_size)) + End Function + + End Class +End Namespace diff --git a/libs/nixio/axTLS/config/.config b/libs/nixio/axTLS/config/.config new file mode 100644 index 000000000..beb0d85fc --- /dev/null +++ b/libs/nixio/axTLS/config/.config @@ -0,0 +1,112 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y +CONFIG_PLATFORM_LINUX=y +# CONFIG_PLATFORM_CYGWIN is not set +# CONFIG_PLATFORM_WIN32 is not set + +# +# General Configuration +# +PREFIX="/usr" +# CONFIG_DEBUG is not set +CONFIG_STRIP_UNWANTED_SECTIONS=y +# CONFIG_VISUAL_STUDIO_7_0 is not set +# CONFIG_VISUAL_STUDIO_8_0 is not set +CONFIG_VISUAL_STUDIO_7_0_BASE="" +CONFIG_VISUAL_STUDIO_8_0_BASE="" +CONFIG_EXTRA_CFLAGS_OPTIONS="-fpic" +CONFIG_EXTRA_LDFLAGS_OPTIONS="" + +# +# SSL Library +# +# CONFIG_SSL_SERVER_ONLY is not set +# CONFIG_SSL_CERT_VERIFICATION is not set +CONFIG_SSL_ENABLE_CLIENT=y +# CONFIG_SSL_FULL_MODE is not set +# CONFIG_SSL_SKELETON_MODE is not set +# CONFIG_SSL_PROT_LOW is not set +CONFIG_SSL_PROT_MEDIUM=y +# CONFIG_SSL_PROT_HIGH is not set +CONFIG_SSL_USE_DEFAULT_KEY=y +CONFIG_SSL_PRIVATE_KEY_LOCATION="" +CONFIG_SSL_PRIVATE_KEY_PASSWORD="" +CONFIG_SSL_X509_CERT_LOCATION="" +CONFIG_SSL_GENERATE_X509_CERT=y +CONFIG_SSL_X509_COMMON_NAME="" +CONFIG_SSL_X509_ORGANIZATION_NAME="" +CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME="" +CONFIG_SSL_ENABLE_V23_HANDSHAKE=y +CONFIG_SSL_HAS_PEM=y +# CONFIG_SSL_USE_PKCS12 is not set +CONFIG_SSL_EXPIRY_TIME=24 +CONFIG_X509_MAX_CA_CERTS=4 +CONFIG_SSL_MAX_CERTS=2 +CONFIG_SSL_CTX_MUTEXING=y +CONFIG_USE_DEV_URANDOM=y +# CONFIG_WIN32_USE_CRYPTO_LIB is not set +# CONFIG_OPENSSL_COMPATIBLE is not set +# CONFIG_PERFORMANCE_TESTING is not set +# CONFIG_SSL_TEST is not set +# CONFIG_AXHTTPD is not set +# CONFIG_HTTP_STATIC_BUILD is not set +CONFIG_HTTP_PORT=0 +CONFIG_HTTP_HTTPS_PORT=0 +CONFIG_HTTP_SESSION_CACHE_SIZE=0 +CONFIG_HTTP_WEBROOT="" +CONFIG_HTTP_TIMEOUT=0 +# CONFIG_HTTP_HAS_CGI is not set +CONFIG_HTTP_CGI_EXTENSIONS="" +# CONFIG_HTTP_ENABLE_LUA is not set +CONFIG_HTTP_LUA_PREFIX="" +CONFIG_HTTP_LUA_CGI_LAUNCHER="" +# CONFIG_HTTP_BUILD_LUA is not set +# CONFIG_HTTP_DIRECTORIES is not set +# CONFIG_HTTP_HAS_AUTHORIZATION is not set +# CONFIG_HTTP_HAS_IPV6 is not set +# CONFIG_HTTP_ENABLE_DIFFERENT_USER is not set +CONFIG_HTTP_USER="" +# CONFIG_HTTP_VERBOSE is not set +# CONFIG_HTTP_IS_DAEMON is not set + +# +# Language Bindings +# +# CONFIG_BINDINGS is not set +# CONFIG_CSHARP_BINDINGS is not set +# CONFIG_VBNET_BINDINGS is not set +CONFIG_DOT_NET_FRAMEWORK_BASE="" +# CONFIG_JAVA_BINDINGS is not set +CONFIG_JAVA_HOME="" +# CONFIG_PERL_BINDINGS is not set +CONFIG_PERL_CORE="" +CONFIG_PERL_LIB="" +# CONFIG_LUA_BINDINGS is not set +CONFIG_LUA_CORE="" + +# +# Samples +# +# CONFIG_SAMPLES is not set +# CONFIG_C_SAMPLES is not set +# CONFIG_CSHARP_SAMPLES is not set +# CONFIG_VBNET_SAMPLES is not set +# CONFIG_JAVA_SAMPLES is not set +# CONFIG_PERL_SAMPLES is not set +# CONFIG_LUA_SAMPLES is not set + +# +# BigInt Options +# +# CONFIG_BIGINT_CLASSICAL is not set +# CONFIG_BIGINT_MONTGOMERY is not set +CONFIG_BIGINT_BARRETT=y +CONFIG_BIGINT_CRT=y +# CONFIG_BIGINT_KARATSUBA is not set +MUL_KARATSUBA_THRESH=0 +SQU_KARATSUBA_THRESH=0 +CONFIG_BIGINT_SLIDING_WINDOW=y +CONFIG_BIGINT_SQUARE=y +# CONFIG_BIGINT_CHECK_ON is not set diff --git a/libs/nixio/axTLS/config/Config.in b/libs/nixio/axTLS/config/Config.in new file mode 100644 index 000000000..dc4075646 --- /dev/null +++ b/libs/nixio/axTLS/config/Config.in @@ -0,0 +1,114 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/config/Kconfig-language.txt +# + +mainmenu "axTLS Configuration" + +config HAVE_DOT_CONFIG + bool + default y + +choice + prompt "Platform" + default CONFIG_PLATFORM_LINUX + +config CONFIG_PLATFORM_LINUX + bool "Linux" + +config CONFIG_PLATFORM_CYGWIN + bool "Cygwin" + +config CONFIG_PLATFORM_WIN32 + bool "Win32" + +endchoice + +menu "General Configuration" + +config PREFIX + string "axTLS installation prefix" + depends on !CONFIG_PLATFORM_WIN32 + default "/usr/local" + help + Define your directory to install axTLS files/subdirs in. + +config CONFIG_DEBUG + bool "Build axTLS with Debugging symbols" + default n + help + Say Y here if you wish to compile axTLS with debugging symbols. + This will allow you to use a debugger to examine axTLS internals. + This increases the size of the binary considerably and should only be + used when doing development. + If you are doing development and want to debug axTLS, answer Y. + + Most people should answer N. + +config CONFIG_STRIP_UNWANTED_SECTIONS + depends on !CONFIG_PLATFORM_WIN32 && !CONFIG_DEBUG + bool "Strip unwanted sections from elf binaries" + default y + help + Strip unwanted sections from the resulting binaries + +menu "Microsoft Compiler Options" +depends on CONFIG_PLATFORM_WIN32 + +choice + prompt "Compiler" + depends on CONFIG_PLATFORM_WIN32 + default CONFIG_VISUAL_STUDIO_7_0 + +config CONFIG_VISUAL_STUDIO_7_0 + bool "Visual Studio 7.0 (2003)" + help + Use Microsoft's Visual Studio 2003 platform. + +config CONFIG_VISUAL_STUDIO_8_0 + bool "Visual Studio 8.0 (2005)" + help + Use Microsoft's Visual Studio 2005 platform. + +endchoice + +config CONFIG_VISUAL_STUDIO_7_0_BASE + string "Base" + depends on CONFIG_VISUAL_STUDIO_7_0 + default "c:\\Program Files\\Microsoft Visual Studio .NET 2003" + +config CONFIG_VISUAL_STUDIO_8_0_BASE + string "Base" + depends on CONFIG_VISUAL_STUDIO_8_0 + default "c:\\Program Files\\Microsoft Visual Studio 8" + +endmenu + +config CONFIG_EXTRA_CFLAGS_OPTIONS + string "Any extra CFLAGS options for the compiler?" + help + Do you want to pass any extra CFLAGS options to the compiler as + you build axTLS? If so, this is the option for you... For + example, if you want to add some simple compiler switches (like + -march=i686), or check for warnings using -Werror, just those + options here. + +config CONFIG_EXTRA_LDFLAGS_OPTIONS + string "Any extra LDFLAGS options for the compiler?" + help + Do you want to pass any extra LDFLAGS options to the compiler? + +endmenu + +source ssl/Config.in +config CONFIG_AXHTTPD + bool "Enable HTTP/HTTPS Web Server" + default y + help + Build the AXHTTPD web server + +source httpd/Config.in +source bindings/Config.in +source samples/Config.in +source ssl/BigIntConfig.in + diff --git a/libs/nixio/axTLS/config/JMeter.jmx b/libs/nixio/axTLS/config/JMeter.jmx new file mode 100755 index 000000000..f62c03f0e --- /dev/null +++ b/libs/nixio/axTLS/config/JMeter.jmx @@ -0,0 +1,247 @@ + + + + + + + + true + false + + + + + 1152004173000 + + + 16 + false + + 10 + false + + 1152004173000 + stopthread + 0 + + + + /index.html + GET + true + + true + 80 + + + + + + false + 127.0.0.1 + + false + + + + + 1152004173000 + + + 16 + false + + 10 + false + + 1152004173000 + stopthread + 0 + + + + /index.html + GET + true + HTTPS + true + 443 + + + + + + false + 127.0.0.1 + + false + + + + + 1152004173000 + + + 16 + false + + 10 + false + + 1152004173000 + stopthread + 0 + + + + /index.html + GET + true + HTTPS + true + 2443 + + + + + + false + 127.0.0.1 + + false + + + + + 1152004173000 + + + 16 + false + + 10 + false + + 1152004173000 + stopthread + 0 + + + + /index.html + GET + true + HTTPS + true + 3443 + + + + + + false + 127.0.0.1 + + false + + + + + 1152004173000 + + + 16 + false + + 10 + false + + 1152004173000 + stopthread + 0 + + + + /index.html + GET + true + HTTPS + true + 1443 + + + + + + false + 127.0.0.1 + + false + + + + + + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + + saveConfig + + + false + + + + + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + + saveConfig + + + false + + + + + diff --git a/libs/nixio/axTLS/config/Rules.mak b/libs/nixio/axTLS/config/Rules.mak new file mode 100644 index 000000000..c0308da05 --- /dev/null +++ b/libs/nixio/axTLS/config/Rules.mak @@ -0,0 +1,220 @@ +# Rules.make for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see the file LICENSE in this tarball for details. +# + +# Pull in the user's busybox configuration +ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),) +-include $(top_builddir)/.config +endif + +#-------------------------------------------------------- +PROG := busybox +MAJOR_VERSION :=1 +MINOR_VERSION :=1 +SUBLEVEL_VERSION:=0 +EXTRAVERSION := +VERSION :=$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBLEVEL_VERSION)$(EXTRAVERSION) +BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z") + + +#-------------------------------------------------------- +# With a modern GNU make(1) (highly recommended, that's what all the +# developers use), all of the following configuration values can be +# overridden at the command line. For example: +# make CROSS=powerpc-linux- top_srcdir="$HOME/busybox" PREFIX=/mnt/app +#-------------------------------------------------------- + +# If you are running a cross compiler, you will want to set 'CROSS' +# to something more interesting... Target architecture is determined +# by asking the CC compiler what arch it compiles things for, so unless +# your compiler is broken, you should not need to specify TARGET_ARCH +CROSS =$(subst ",, $(strip $(CROSS_COMPILER_PREFIX))) +CC = $(CROSS)gcc +AR = $(CROSS)ar +AS = $(CROSS)as +LD = $(CROSS)ld +NM = $(CROSS)nm +STRIP = $(CROSS)strip +CPP = $(CC) -E +# MAKEFILES = $(top_builddir)/.config +RM = rm +RM_F = $(RM) -f +LN = ln +LN_S = $(LN) -s +MKDIR = mkdir +MKDIR_P = $(MKDIR) -p +MV = mv +CP = cp + + +# What OS are you compiling busybox for? This allows you to include +# OS specific things, syscall overrides, etc. +TARGET_OS=linux + +# Select the compiler needed to build binaries for your development system +HOSTCC = gcc +HOSTCFLAGS= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer + +# Ensure consistent sort order, 'gcc -print-search-dirs' behavior, etc. +LC_ALL:= C + +# If you want to add some simple compiler switches (like -march=i686), +# especially from the command line, use this instead of CFLAGS directly. +# For optimization overrides, it's better still to set OPTIMIZATION. +CFLAGS_EXTRA=$(subst ",, $(strip $(EXTRA_CFLAGS_OPTIONS))) + +# To compile vs some other alternative libc, you may need to use/adjust +# the following lines to meet your needs... +# +# If you are using Red Hat 6.x with the compatible RPMs (for developing under +# Red Hat 5.x and glibc 2.0) uncomment the following. Be sure to read about +# using the compatible RPMs (compat-*) at http://www.redhat.com ! +#LIBCDIR:=/usr/i386-glibc20-linux +# +# For other libraries, you are on your own. But these may (or may not) help... +#LDFLAGS+=-nostdlib +#LIBRARIES:=$(LIBCDIR)/lib/libc.a -lgcc +#CROSS_CFLAGS+=-nostdinc -I$(LIBCDIR)/include -I$(GCCINCDIR) -funsigned-char +#GCCINCDIR:=$(shell gcc -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp") + +WARNINGS=-Wall -Wstrict-prototypes -Wshadow +CFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir) +ARFLAGS=cru + + +# gcc centric. Perhaps fiddle with findstring gcc,$(CC) for the rest +# get the CC MAJOR/MINOR version +CC_MAJOR:=$(shell printf "%02d" $(shell echo __GNUC__ | $(CC) -E -xc - | tail -n 1)) +CC_MINOR:=$(shell printf "%02d" $(shell echo __GNUC_MINOR__ | $(CC) -E -xc - | tail -n 1)) + +#-------------------------------------------------------- +export VERSION BUILDTIME HOSTCC HOSTCFLAGS CROSS CC AR AS LD NM STRIP CPP +ifeq ($(strip $(TARGET_ARCH)),) +TARGET_ARCH:=$(shell $(CC) -dumpmachine | sed -e s'/-.*//' \ + -e 's/i.86/i386/' \ + -e 's/sparc.*/sparc/' \ + -e 's/arm.*/arm/g' \ + -e 's/m68k.*/m68k/' \ + -e 's/ppc/powerpc/g' \ + -e 's/v850.*/v850/g' \ + -e 's/sh[234]/sh/' \ + -e 's/mips-.*/mips/' \ + -e 's/mipsel-.*/mipsel/' \ + -e 's/cris.*/cris/' \ + ) +endif + +# A nifty macro to make testing gcc features easier +check_gcc=$(shell \ + if [ "$(1)" != "" ]; then \ + if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \ + then echo "$(1)"; else echo "$(2)"; fi \ + fi) + +# Setup some shortcuts so that silent mode is silent like it should be +ifeq ($(subst s,,$(MAKEFLAGS)),$(MAKEFLAGS)) +export MAKE_IS_SILENT=n +SECHO=@echo +else +export MAKE_IS_SILENT=y +SECHO=-@false +endif + +CFLAGS+=$(call check_gcc,-funsigned-char,) + +#-------------------------------------------------------- +# Arch specific compiler optimization stuff should go here. +# Unless you want to override the defaults, do not set anything +# for OPTIMIZATION... + +# use '-Os' optimization if available, else use -O2 +OPTIMIZATION:=$(call check_gcc,-Os,-O2) + +# Some nice architecture specific optimizations +ifeq ($(strip $(TARGET_ARCH)),arm) + OPTIMIZATION+=-fstrict-aliasing +endif +ifeq ($(strip $(TARGET_ARCH)),i386) + OPTIMIZATION+=$(call check_gcc,-march=i386,) + OPTIMIZATION+=$(call check_gcc,-mpreferred-stack-boundary=2,) + OPTIMIZATION+=$(call check_gcc,-falign-functions=0 -falign-jumps=0 -falign-loops=0,\ + -malign-functions=0 -malign-jumps=0 -malign-loops=0) +endif +OPTIMIZATIONS:=$(OPTIMIZATION) -fomit-frame-pointer + +# +#-------------------------------------------------------- +# If you're going to do a lot of builds with a non-vanilla configuration, +# it makes sense to adjust parameters above, so you can type "make" +# by itself, instead of following it by the same half-dozen overrides +# every time. The stuff below, on the other hand, is probably less +# prone to casual user adjustment. +# + +ifeq ($(strip $(CONFIG_LFS)),y) + # For large file summit support + CFLAGS+=-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +endif +ifeq ($(strip $(CONFIG_DMALLOC)),y) + # For testing mem leaks with dmalloc + CFLAGS+=-DDMALLOC + LIBRARIES:=-ldmalloc +else + ifeq ($(strip $(CONFIG_EFENCE)),y) + LIBRARIES:=-lefence + endif +endif +ifeq ($(strip $(CONFIG_DEBUG)),y) + CFLAGS +=$(WARNINGS) -g -D_GNU_SOURCE + LDFLAGS +=-Wl,-warn-common + STRIPCMD:=/bin/true -Not_stripping_since_we_are_debugging +else + CFLAGS+=$(WARNINGS) $(OPTIMIZATIONS) -D_GNU_SOURCE -DNDEBUG + LDFLAGS += -Wl,-warn-common + STRIPCMD:=$(STRIP) -s --remove-section=.note --remove-section=.comment +endif +ifeq ($(strip $(CONFIG_STATIC)),y) + LDFLAGS += --static +endif + +ifeq ($(strip $(CONFIG_SELINUX)),y) + LIBRARIES += -lselinux +endif + +ifeq ($(strip $(PREFIX)),) + PREFIX:=`pwd`/_install +endif + +# Additional complications due to support for pristine source dir. +# Include files in the build directory should take precedence over +# the copy in top_srcdir, both during the compilation phase and the +# shell script that finds the list of object files. +# Work in progress by . + + +OBJECTS:=$(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o +CFLAGS += $(CROSS_CFLAGS) +ifdef BB_INIT_SCRIPT + CFLAGS += -DINIT_SCRIPT='"$(BB_INIT_SCRIPT)"' +endif + +# Put user-supplied flags at the end, where they +# have a chance of winning. +CFLAGS += $(CFLAGS_EXTRA) + +#------------------------------------------------------------ +# Installation options +ifeq ($(strip $(CONFIG_INSTALL_APPLET_HARDLINKS)),y) +INSTALL_OPTS=--hardlinks +endif +ifeq ($(strip $(CONFIG_INSTALL_APPLET_SYMLINKS)),y) +INSTALL_OPTS=--symlinks +endif +ifeq ($(strip $(CONFIG_INSTALL_APPLET_DONT)),y) +INSTALL_OPTS= +endif + +.PHONY: dummy diff --git a/libs/nixio/axTLS/config/axhttpd.aip b/libs/nixio/axTLS/config/axhttpd.aip new file mode 100755 index 000000000..412fe3b4d --- /dev/null +++ b/libs/nixio/axTLS/config/axhttpd.aip @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/nixio/axTLS/config/axtls.RES b/libs/nixio/axTLS/config/axtls.RES new file mode 100644 index 0000000000000000000000000000000000000000..2929b3b679fddfb78bb58ebbccc5c0c4a9eb37d8 GIT binary patch literal 22748 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 /dev/null 2>&1; then \ + echo "Mono not installed! - go " \ + "to http://www.mono-project.com/Main_Page" && exit 1; \ + fi + +endif # Linux + +endif # not 'clean' diff --git a/libs/nixio/axTLS/config/makefile.java.conf b/libs/nixio/axTLS/config/makefile.java.conf new file mode 100644 index 000000000..9b2246290 --- /dev/null +++ b/libs/nixio/axTLS/config/makefile.java.conf @@ -0,0 +1,58 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +ifneq ($(MAKECMDGOALS), clean) + +ifdef CONFIG_PLATFORM_CYGWIN +CFLAGS += -I"$(CONFIG_JAVA_HOME)/include" +CFLAGS += -I"$(CONFIG_JAVA_HOME)/include/win32" +JAVA_BIN:=$(CONFIG_JAVA_HOME)/bin +else + +ifdef CONFIG_PLATFORM_WIN32 +CFLAGS += /I"$(shell cygpath -w $(CONFIG_JAVA_HOME)/include)" +CFLAGS += /I"$(shell cygpath -w $(CONFIG_JAVA_HOME)/include/win32)" +JAVA_BIN:=$(shell cygpath -u $(CONFIG_JAVA_HOME)/bin) +else # Linux +CFLAGS += -I$(CONFIG_JAVA_HOME)/include + +ifdef CONFIG_PLATFORM_SOLARIS +CFLAGS += -I$(CONFIG_JAVA_HOME)/include/solaris +else +CFLAGS += -I$(CONFIG_JAVA_HOME)/include/linux +endif + +JAVA_BIN:=$(CONFIG_JAVA_HOME)/bin +endif +endif + +PATH:=$(JAVA_BIN):$(PATH) + +endif # not 'clean' diff --git a/libs/nixio/axTLS/config/makefile.post b/libs/nixio/axTLS/config/makefile.post new file mode 100644 index 000000000..033981c4d --- /dev/null +++ b/libs/nixio/axTLS/config/makefile.post @@ -0,0 +1,19 @@ + +ifneq ($(MAKECMDGOALS), clean) +ifndef CONFIG_PLATFORM_WIN32 +ifndef CONFIG_PLATFORM_SOLARIS +# do dependencies +-include .depend +all : .depend +.depend: $(wildcard *.c) + @$(CC) $(CFLAGS) -MM $^ > $@ +endif # 'not' solaris +endif # 'not' win32 + +ifdef CONFIG_PLATFORM_WIN32 +OBJ:=$(OBJ:.o=.obj) +%.obj : %.c + $(CC) $(CFLAGS) $< +endif # win32 + +endif # end of 'not' clean diff --git a/libs/nixio/axTLS/config/scripts/config/Kconfig-language.txt b/libs/nixio/axTLS/config/scripts/config/Kconfig-language.txt new file mode 100644 index 000000000..493749b32 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/Kconfig-language.txt @@ -0,0 +1,255 @@ +Introduction +------------ + +The configuration database is collection of configuration options +organized in a tree structure: + + +- Code maturity level options + | +- Prompt for development and/or incomplete code/drivers + +- General setup + | +- Networking support + | +- System V IPC + | +- BSD Process Accounting + | +- Sysctl support + +- Loadable module support + | +- Enable loadable module support + | +- Set version information on all module symbols + | +- Kernel module loader + +- ... + +Every entry has its own dependencies. These dependencies are used +to determine the visible of an entry. Any child entry is only +visible if its parent entry is also visible. + +Menu entries +------------ + +Most entries define a config option, all other entries help to organize +them. A single configuration option is defined like this: + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + help + Usually, modules have to be recompiled whenever you switch to a new + kernel. ... + +Every line starts with a key word and can be followed by multiple +arguments. "config" starts a new config entry. The following lines +define attributes for this config option. Attributes can be the type of +the config option, input prompt, dependencies, help text and default +values. A config option can be defined multiple times with the same +name, but every definition can have only a single input prompt and the +type must not conflict. + +Menu attributes +--------------- + +A menu entry can have a number of attributes. Not all of them are +applicable everywhere (see syntax). + +- type definition: "bool"/"tristate"/"string"/"hex"/"integer" + Every config option must have a type. There are only two basic types: + tristate and string, the other types base on these two. The type + definition optionally accepts an input prompt, so these two examples + are equivalent: + + bool "Networking support" + and + bool + prompt "Networking support" + +- input prompt: "prompt" ["if" ] + Every menu entry can have at most one prompt, which is used to display + to the user. Optionally dependencies only for this prompt can be added + with "if". + +- default value: "default" ["if" ] + A config option can have any number of default values. If multiple + default values are visible, only the first defined one is active. + Default values are not limited to the menu entry, where they are + defined, this means the default can be defined somewhere else or be + overriden by an earlier definition. + The default value is only assigned to the config symbol if no other + value was set by the user (via the input prompt above). If an input + prompt is visible the default value is presented to the user and can + be overridden by him. + Optionally dependencies only for this default value can be added with + "if". + +- dependencies: "depends on"/"requires" + This defines a dependency for this menu entry. If multiple + dependencies are defined they are connected with '&&'. Dependencies + are applied to all other options within this menu entry (which also + accept "if" expression), so these two examples are equivalent: + + bool "foo" if BAR + default y if BAR + and + depends on BAR + bool "foo" + default y + +- help text: "help" + This defines a help text. The end of the help text is determined by + the level indentation, this means it ends at the first line which has + a smaller indentation than the first line of the help text. + + +Menu dependencies +----------------- + +Dependencies define the visibility of a menu entry and can also reduce +the input range of tristate symbols. The tristate logic used in the +expressions uses one more state than normal boolean logic to express the +module state. Dependency expressions have the following syntax: + + ::= (1) + '=' (2) + '!=' (3) + '(' ')' (4) + '!' (5) + '||' (6) + '&&' (7) + +Expressions are listed in decreasing order of precedence. + +(1) Convert the symbol into an expression. Boolean and tristate symbols + are simply converted into the respective expression values. All + other symbol types result in 'n'. +(2) If the values of both symbols are equal, it returns 'y', + otherwise 'n'. +(3) If the values of both symbols are equal, it returns 'n', + otherwise 'y'. +(4) Returns the value of the expression. Used to override precedence. +(5) Returns the result of (2-/expr/). +(6) Returns the result of min(/expr/, /expr/). +(7) Returns the result of max(/expr/, /expr/). + +An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2 +respectively for calculations). A menu entry becomes visible when it's +expression evaluates to 'm' or 'y'. + +There are two type of symbols: constant and nonconstant symbols. +Nonconstant symbols are the most common ones and are defined with the +'config' statement. Nonconstant symbols consist entirely of alphanumeric +characters or underscores. +Constant symbols are only part of expressions. Constant symbols are +always surrounded by single or double quotes. Within the quote any +other character is allowed and the quotes can be escaped using '\'. + +Menu structure +-------------- + +The position of a menu entry in the tree is determined in two ways. First +it can be specified explicitely: + +menu "Network device support" + depends NET + +config NETDEVICES + ... + +endmenu + +All entries within the "menu" ... "endmenu" block become a submenu of +"Network device support". All subentries inherit the dependencies from +the menu entry, e.g. this means the dependency "NET" is added to the +dependency list of the config option NETDEVICES. + +The other way to generate the menu structure is done by analyzing the +dependencies. If a menu entry somehow depends on the previous entry, it +can be made a submenu of it. First the the previous (parent) symbol must +be part of the dependency list and then one of these two condititions +must be true: +- the child entry must become invisible, if the parent is set to 'n' +- the child entry must only be visible, if the parent is visible + +config MODULES + bool "Enable loadable module support" + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + +comment "module support disabled" + depends !MODULES + +MODVERSIONS directly depends on MODULES, this means it's only visible if +MODULES is different from 'n'. The comment on the other hand is always +visible when MODULES it's visible (the (empty) dependency of MODULES is +also part of the comment dependencies). + + +Kconfig syntax +-------------- + +The configuration file describes a series of menu entries, where every +line starts with a keyword (except help texts). The following keywords +end a menu entry: +- config +- choice/endchoice +- comment +- menu/endmenu +- if/endif +- source +The first four also start the definition of a menu entry. + +config: + + "config" + + +This defines a config symbol and accepts any of above +attributes as options. + +choices: + + "choice" + + + "endchoice" + +This defines a choice group and accepts any of above attributes as +options. A choice can only be of type bool or tristate, while a boolean +choice only allows a single config entry to be selected, a tristate +choice also allows any number of config entries to be set to 'm'. This +can be used if multiple drivers for a single hardware exists and only a +single driver can be compiled/loaded into the kernel, but all drivers +can be compiled as modules. +A choice accepts another option "optional", which allows to set the +choice to 'n' and no entry needs to be selected. + +comment: + + "comment" + + +This defines a comment which is displayed to the user during the +configuration process and is also echoed to the output files. The only +possible options are dependencies. + +menu: + + "menu" +

+ + "endmenu" + +This defines a menu block, see "Menu structure" above for more +information. The only possible options are dependencies. + +if: + + "if" + + "endif" + +This defines an if block. The dependency expression is appended +to all enclosed menu entries. + +source: + + "source" + +This reads the specified configuration file. This file is always parsed. diff --git a/libs/nixio/axTLS/config/scripts/config/Makefile b/libs/nixio/axTLS/config/scripts/config/Makefile new file mode 100644 index 000000000..739950163 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/Makefile @@ -0,0 +1,121 @@ +# Makefile for axTLS +# +# Copyright (C) 2002 Erik Andersen + +top_srcdir=../.. +top_builddir=../.. +srcdir=$(top_srcdir)/scripts/config +include $(top_srcdir)/Rules.mak + +all: ncurses conf mconf + +ifeq ($(shell uname),SunOS) +LIBS = -lcurses +else +LIBS = -lncurses +endif +ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h)) + HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h)) + HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/local/include/ncurses/ncurses.h, $(wildcard /usr/local/include/ncurses/ncurses.h)) + HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/local/include/ncurses/curses.h, $(wildcard /usr/local/include/ncurses/curses.h)) + HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="" +else +ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h)) + HOSTNCURSES += -DCURSES_LOC="" +else + HOSTNCURSES += -DCURSES_LOC="" +endif +endif +endif +endif +endif + +CONF_SRC = conf.c +MCONF_SRC = mconf.c +LXD_SRC = lxdialog/checklist.c lxdialog/menubox.c lxdialog/textbox.c \ + lxdialog/yesno.c lxdialog/inputbox.c lxdialog/util.c \ + lxdialog/msgbox.c + +SHARED_SRC = zconf.tab.c +SHARED_DEPS := $(srcdir)/lkc.h $(srcdir)/lkc_proto.h \ + lkc_defs.h $(srcdir)/expr.h zconf.tab.h +CONF_OBJS = $(patsubst %.c,%.o, $(CONF_SRC)) +MCONF_OBJS = $(patsubst %.c,%.o, $(MCONF_SRC) $(LXD_SRC)) +SHARED_OBJS = $(patsubst %.c,%.o, $(SHARED_SRC)) + +conf: $(CONF_OBJS) $(SHARED_OBJS) + $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ + +mconf: $(MCONF_OBJS) $(SHARED_OBJS) + $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ $(LIBS) + +$(CONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I. -c $< -o $@ + +$(MCONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS) + @[ -d $(@D) ] || mkdir -v $(@D) + $(HOSTCC) $(HOSTCFLAGS) $(HOSTNCURSES) -I. -c $< -o $@ + +lkc_defs.h: $(srcdir)/lkc_proto.h + @sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/' + +### +# The following requires flex/bison +# By default we use the _shipped versions, uncomment the +# following line if you are modifying the flex/bison src. +#LKC_GENPARSER := 1 + +ifdef LKC_GENPARSER + +%.tab.c %.tab.h: $(srcdir)/%.y + bison -t -d -v -b $* -p $(notdir $*) $< + +lex.%.c: $(srcdir)/%.l + flex -P$(notdir $*) -o$@ $< +else + +lex.zconf.o: lex.zconf.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -c $< -o $@ + +lex.zconf.c: $(srcdir)/lex.zconf.c_shipped + cp $< $@ + +zconf.tab.c: $(srcdir)/zconf.tab.c_shipped + cp $< $@ + +zconf.tab.h: $(srcdir)/zconf.tab.h_shipped + cp $< $@ +endif + +zconf.tab.o: zconf.tab.c lex.zconf.c $(srcdir)/confdata.c $(srcdir)/expr.c \ + $(srcdir)/symbol.c $(srcdir)/menu.c $(SHARED_DEPS) + $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -I. -c $< -o $@ + +.PHONY: ncurses + +ncurses: + @echo "main() {}" > lxtemp.c + @if $(HOSTCC) lxtemp.c $(LIBS) ; then \ + rm -f lxtemp.c a.out; \ + else \ + rm -f lxtemp.c; \ + echo -e "\007" ;\ + echo ">> Unable to find the Ncurses libraries." ;\ + echo ">>" ;\ + echo ">> You must have Ncurses installed in order" ;\ + echo ">> to use 'make menuconfig'" ;\ + echo ;\ + exit 1 ;\ + fi + +clean: + rm -f *.o *~ ../../*~ core *.exe $(TARGETS) $(MCONF_OBJS) $(CONF_OBJS) + rm -f conf conf.exe mconf mconf.exe zconf.tab.c zconf.tab.h lex.zconf.c lkc_defs.h + rm -f ../..config.h + diff --git a/libs/nixio/axTLS/config/scripts/config/conf.c b/libs/nixio/axTLS/config/scripts/config/conf.c new file mode 100644 index 000000000..15244678e --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/conf.c @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); + +enum { + ask_all, + ask_new, + ask_silent, + set_default, + set_yes, + set_mod, + set_no, + set_random +} input_mode = ask_all; +char *defconfig_file; + +static int indent = 1; +static int valid_stdin = 1; +static int conf_cnt; +static char line[128]; +static struct menu *rootEntry; + +static char nohelp_text[] = "Sorry, no help available for this option yet.\n"; + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin && input_mode == ask_silent) { + printf("aborted!\n\n"); + printf("Console input/output is redirected. "); + printf("Run 'make oldconfig' to update configuration.\n\n"); + exit(1); + } +} + +static void conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + tristate val; + + if (!sym_has_value(sym)) + printf("(NEW) "); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return; + } + + switch (input_mode) { + case ask_new: + case ask_silent: + if (sym_has_value(sym)) { + printf("%s\n", def); + return; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + return; + case set_default: + printf("%s\n", def); + return; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return; + default: + ; + } + switch (input_mode) { + case set_yes: + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_mod: + if (type == S_TRISTATE) { + if (sym_tristate_within_range(sym, mod)) { + line[0] = 'm'; + line[1] = '\n'; + line[2] = 0; + break; + } + } else { + if (sym_tristate_within_range(sym, yes)) { + line[0] = 'y'; + line[1] = '\n'; + line[2] = 0; + break; + } + } + case set_no: + if (sym_tristate_within_range(sym, no)) { + line[0] = 'n'; + line[1] = '\n'; + line[2] = 0; + break; + } + case set_random: + do { + val = (tristate)(random() % 3); + } while (!sym_tristate_within_range(sym, val)); + switch (val) { + case no: line[0] = 'n'; break; + case mod: line[0] = 'm'; break; + case yes: line[0] = 'y'; break; + } + line[1] = '\n'; + line[2] = 0; + break; + default: + break; + } + printf("%s", line); +} + +int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def, *help; + + while (1) { + printf("%*s%s ", indent - 1, "", menu->prompt->text); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + conf_askvalue(sym, def); + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + help = nohelp_text; + if (menu->sym->help) + help = menu->sym->help; + printf("\n%s\n", menu->sym->help); + def = NULL; + break; + } + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + int type; + tristate oldval, newval; + const char *help; + + while (1) { + printf("%*s%s ", indent - 1, "", menu->prompt->text); + if (sym->name) + printf("(%s) ", sym->name); + type = sym_get_type(sym); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (sym->help) + printf("/?"); + printf("] "); + conf_askvalue(sym, sym_get_string_value(sym)); + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + help = nohelp_text; + if (sym->help) + help = sym->help; + printf("\n%s\n", help); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + int type; + bool is_new; + + sym = menu->sym; + type = sym_get_type(sym); + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = '0'; + line[1] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', menu_get_prompt(child)); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, menu_get_prompt(child)); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(" (NEW)"); + printf("\n"); + } + printf("%*schoice", indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (sym->help) + printf("?"); + printf("]: "); + switch (input_mode) { + case ask_new: + case ask_silent: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + case ask_all: + fflush(stdout); + fgets(line, 128, stdin); + strip(line); + if (line[0] == '?') { + printf("\n%s\n", menu->sym->help ? + menu->sym->help : nohelp_text); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + case set_random: + def = (random() % cnt) + 1; + case set_default: + case set_yes: + case set_mod: + case set_no: + cnt = def; + printf("%d\n", cnt); + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[strlen(line) - 1] == '?') { + printf("\n%s\n", child->sym->help ? + child->sym->help : nohelp_text); + continue; + } + sym_set_choice_value(sym, child->sym); + if (child->list) { + indent += 2; + conf(child->list); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if (input_mode == ask_silent && rootEntry != menu) { + check_conf(menu); + return; + } + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', prompt, + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym) { + if (sym_is_changable(sym) && !sym_has_value(sym)) { + if (!conf_cnt++) + printf("*\n* Restart config...\n*\n"); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + if (sym_is_choice(sym) && sym_get_tristate_value(sym) != mod) + return; + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +int main(int ac, char **av) +{ + int i = 1; + const char *name; + struct stat tmpstat; + + if (ac > i && av[i][0] == '-') { + switch (av[i++][1]) { + case 'o': + input_mode = ask_new; + break; + case 's': + input_mode = ask_silent; + valid_stdin = isatty(0) && isatty(1) && isatty(2); + break; + case 'd': + input_mode = set_default; + break; + case 'D': + input_mode = set_default; + defconfig_file = av[i++]; + if (!defconfig_file) { + printf("%s: No default config file specified\n", + av[0]); + exit(1); + } + break; + case 'n': + input_mode = set_no; + break; + case 'm': + input_mode = set_mod; + break; + case 'y': + input_mode = set_yes; + break; + case 'r': + input_mode = set_random; + srandom(time(NULL)); + break; + case 'h': + case '?': + printf("%s [-o|-s] config\n", av[0]); + exit(0); + } + } + name = av[i]; + if (!name) { + printf("%s: configuration file missing\n", av[0]); + } + conf_parse(name); + //zconfdump(stdout); + switch (input_mode) { + case set_default: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf("***\n" + "*** Can't find default configuration \"%s\"!\n" + "***\n", defconfig_file); + exit(1); + } + break; + case ask_silent: + if (stat(".config", &tmpstat)) { + printf("***\n" + "*** You have not yet configured axTLS!\n" + "***\n" + "*** Please run some configurator (e.g. \"make oldconfig\" or\n" + "*** \"make menuconfig\" or \"make config\").\n" + "***\n"); + exit(1); + } + case ask_all: + case ask_new: + conf_read(NULL); + break; + default: + break; + } + + if (input_mode != ask_silent) { + rootEntry = &rootmenu; + conf(&rootmenu); + if (input_mode == ask_all) { + input_mode = ask_silent; + valid_stdin = 1; + } + } + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt); + if (conf_write(NULL)) { + fprintf(stderr, "\n*** Error during writing of the axTLS configuration.\n\n"); + return 1; + } + return 0; +} diff --git a/libs/nixio/axTLS/config/scripts/config/confdata.c b/libs/nixio/axTLS/config/scripts/config/confdata.c new file mode 100644 index 000000000..a59e24550 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/confdata.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +const char conf_def_filename[] = "config/.config"; + +const char conf_defname[] = "config/defconfig"; + +const char *conf_confnames[] = { + "config/.config", + conf_defname, + NULL, +}; + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +int conf_read(const char *name) +{ + FILE *in = NULL; + char line[1024]; + char *p, *p2; + int lineno = 0; + struct symbol *sym; + struct property *prop; + struct expr *e; + int i; + + if (name) { + in = zconf_fopen(name); + } else { + const char **names = conf_confnames; + while ((name = *names++)) { + name = conf_expand_value(name); + in = zconf_fopen(name); + if (in) { + printf("#\n" + "# using defaults found in %s\n" + "#\n", name); + break; + } + } + } + + if (!in) + return 1; + + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED; + sym->flags &= ~SYMBOL_VALID; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->user.val) + free(sym->user.val); + default: + sym->user.val = NULL; + sym->user.tri = no; + } + } + + while (fgets(line, sizeof(line), in)) { + lineno++; + sym = NULL; + switch (line[0]) { + case '#': + if (line[1]!=' ') + continue; + p = strchr(line + 2, ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + sym = sym_find(line + 2); + if (!sym) { + fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 2); + break; + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->user.tri = no; + sym->flags &= ~SYMBOL_NEW; + break; + default: + ; + } + break; + + case 'A' ... 'Z': + p = strchr(line, '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) + *p2 = 0; + sym = sym_find(line); + if (!sym) { + fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line); + break; + } + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->user.tri = mod; + sym->flags &= ~SYMBOL_NEW; + break; + } + case S_BOOLEAN: + if (p[0] == 'y') { + sym->user.tri = yes; + sym->flags &= ~SYMBOL_NEW; + break; + } + if (p[0] == 'n') { + sym->user.tri = no; + sym->flags &= ~SYMBOL_NEW; + break; + } + break; + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + fprintf(stderr, "%s:%d: invalid string found\n", name, lineno); + exit(1); + } + case S_INT: + case S_HEX: + if (sym_string_valid(sym, p)) { + sym->user.val = strdup(p); + sym->flags &= ~SYMBOL_NEW; + } else { + fprintf(stderr, "%s:%d: symbol value '%s' invalid for %s\n", name, lineno, p, sym->name); + exit(1); + } + break; + default: + ; + } + break; + case '\n': + break; + default: + continue; + } + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->user.tri) { + case no: + break; + case mod: + if (cs->user.tri == yes) + /* warn? */; + break; + case yes: + if (cs->user.tri != no) + /* warn? */; + cs->user.val = sym; + break; + } + cs->user.tri = E_OR(cs->user.tri, sym->user.tri); + cs->flags &= ~SYMBOL_NEW; + } + } + fclose(in); + + if (modules_sym) + sym_calc_value(modules_sym); + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + if (sym->visible == no) + sym->flags |= SYMBOL_NEW; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + if (!sym_string_within_range(sym, sym->user.val)) + sym->flags |= SYMBOL_NEW; + default: + break; + } + } + if (!sym_is_choice(sym)) + continue; + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) + if (e->right.sym->visible != no) + sym->flags |= e->right.sym->flags & SYMBOL_NEW; + } + + sym_change_count = 1; + + return 0; +} + +struct menu *next_menu(struct menu *menu) +{ + if (menu->list) return menu->list; + do { + if (menu->next) { + menu = menu->next; + break; + } + } while ((menu = menu->parent)); + + return menu; +} + +#define SYMBOL_FORCEWRITE (1<<31) + +int conf_write(const char *name) +{ + FILE *out, *out_h; + struct symbol *sym; + struct menu *menu; + const char *basename; + char dirname[128], tmpname[128], newname[128]; + int type, l; + const char *str; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_def_filename; + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_def_filename; + } else + basename = name; + } else + basename = conf_def_filename; + + sprintf(newname, "config/%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(newname, "w"); + if (!out) + return 1; + out_h = NULL; + if (!name) { + out_h = fopen("config/.tmpconfig.h", "w"); + if (!out_h) + return 1; + } + fprintf(out, "#\n" + "# Automatically generated make config: don't edit\n" + "#\n"); + if (out_h) { + fprintf(out_h, "/*\n" + " * Automatically generated header file: don't edit\n" + " */\n\n"); +#if 0 + "/* Version Number */\n" + "#define BB_VER \"%s\"\n" + "#define BB_BT \"%s\"\n", + getenv("VERSION"), + getenv("BUILDTIME")); + if (getenv("EXTRA_VERSION")) + fprintf(out_h, "#define BB_EXTRA_VERSION \"%s\"\n", + getenv("EXTRA_VERSION")); + fprintf(out_h, "\n"); +#endif + } + + if (!sym_change_count) + sym_clear_all_valid(); + + /* Force write of all non-duplicate symbols. */ + + /* Write out everything by default. */ + for(menu = rootmenu.list; menu; menu = next_menu(menu)) + if (menu->sym) menu->sym->flags |= SYMBOL_FORCEWRITE; + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + if (out_h) + fprintf(out_h, "\n" + "/*\n" + " * %s\n" + " */\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_FORCEWRITE)) + goto next; + + sym->flags &= ~SYMBOL_FORCEWRITE; + type = sym->type; + if (type == S_TRISTATE) { + sym_calc_value(modules_sym); + if (modules_sym->curr.tri == no) + type = S_BOOLEAN; + } + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym_get_tristate_value(sym)) { + case no: + fprintf(out, "# %s is not set\n", sym->name); + if (out_h) + fprintf(out_h, "#undef %s\n", sym->name); + break; + case mod: +#if 0 + fprintf(out, "%s=m\n", sym->name); + if (out_h) + fprintf(out_h, "#define %s_MODULE 1\n", sym->name); +#endif + break; + case yes: + fprintf(out, "%s=y\n", sym->name); + if (out_h) + fprintf(out_h, "#define %s 1\n", sym->name); + break; + } + break; + case S_STRING: + // fix me + str = sym_get_string_value(sym); + fprintf(out, "%s=\"", sym->name); + if (out_h) + fprintf(out_h, "#define %s \"", sym->name); + do { + l = strcspn(str, "\"\\"); + if (l) { + fwrite(str, l, 1, out); + if (out_h) + fwrite(str, l, 1, out_h); + } + str += l; + while (*str == '\\' || *str == '"') { + fprintf(out, "\\%c", *str); + if (out_h) + fprintf(out_h, "\\%c", *str); + str++; + } + } while (*str); + fputs("\"\n", out); + if (out_h) + fputs("\"\n", out_h); + break; + case S_HEX: + str = sym_get_string_value(sym); + if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { + fprintf(out, "%s=%s\n", sym->name, *str ? str : "0"); + if (out_h) + fprintf(out_h, "#define %s 0x%s\n", sym->name, str); + break; + } + case S_INT: + str = sym_get_string_value(sym); + fprintf(out, "%s=%s\n", sym->name, *str ? str : "0"); + if (out_h) + fprintf(out_h, "#define %s %s\n", sym->name, str); + break; + } + } +next: + menu = next_menu(menu); + } + fclose(out); + if (out_h) { + fclose(out_h); + rename("config/.tmpconfig.h", "config/config.h"); + file_write_dep(NULL); + } + if (!name || basename != conf_def_filename) { + if (!name) + name = conf_def_filename; + sprintf(tmpname, "%s.old", name); + rename(name, tmpname); + } + sprintf(tmpname, "%s%s", dirname, basename); + if (rename(newname, tmpname)) + return 1; + + sym_change_count = 0; + + return 0; +} diff --git a/libs/nixio/axTLS/config/scripts/config/expr.c b/libs/nixio/axTLS/config/scripts/config/expr.c new file mode 100644 index 000000000..30e4f9d69 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/expr.c @@ -0,0 +1,1099 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define DEBUG_EXPR 0 + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = malloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_CHOICE: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO))) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_CHOICE: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp, *tmp1, *tmp2; + + if (e1->type == type) { + expr_eliminate_dups2(type, &e1->left.expr, &e2); + expr_eliminate_dups2(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups2(type, &e1, &e2->left.expr); + expr_eliminate_dups2(type, &e1, &e2->right.expr); + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO || BAR) && (!FOO && !BAR) -> n + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_and(&tmp1, &tmp2); + if (expr_is_yes(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_no); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + case E_AND: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO && BAR) || (!FOO || !BAR) -> y + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_or(&tmp1, &tmp2); + if (expr_is_no(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_yes); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + expr_eliminate_dups2(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_UNEQUAL: + case E_SYMBOL: + case E_CHOICE: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_AND, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_OR, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + if (e1->type == type) { + expr_extract_eq(type, ep, &e1->left.expr, &e2); + expr_extract_eq(type, ep, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_extract_eq(type, ep, ep1, &e2->left.expr); + expr_extract_eq(type, ep, ep1, &e2->right.expr); + return; + } + if (expr_eq(e1, e2)) { + *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; + expr_free(e2); + if (type == E_AND) { + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + } else if (type == E_OR) { + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + } + } +#undef e1 +#undef e2 +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_CHOICE: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return E_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return E_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return E_NOT(val1); + case E_EQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? yes : no; + case E_UNEQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? no : yes; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } +} + +int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ +#if 0 + return 1; +#else + if (t1 == t2) + return 0; + switch (t1) { + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_CHOICE) + return 1; + case E_CHOICE: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +#endif +} + +void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym->name); + else + fn(data, ""); + break; + case E_NOT: + fn(data, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + fn(data, e->left.sym->name); + fn(data, "="); + fn(data, e->right.sym->name); + break; + case E_UNEQUAL: + fn(data, e->left.sym->name); + fn(data, "!="); + fn(data, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_CHOICE: + fn(data, e->right.sym->name); + if (e->left.expr) { + fn(data, " ^ "); + expr_print(e->left.expr, fn, data, E_CHOICE); + } + break; + case E_RANGE: + fn(data, "["); + fn(data, e->left.sym->name); + fn(data, " "); + fn(data, e->right.sym->name); + fn(data, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "", e->type); + fn(data, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, ")"); +} + +static void expr_print_file_helper(void *data, const char *str) +{ + fwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} + +static void expr_print_gstr_helper(void *data, const char *str) +{ + str_append((struct gstr*)data, str); +} + +void expr_gstr_print(struct expr *e, struct gstr *gs) +{ + expr_print(e, expr_print_gstr_helper, gs, E_NONE); +} diff --git a/libs/nixio/axTLS/config/scripts/config/expr.h b/libs/nixio/axTLS/config/scripts/config/expr.h new file mode 100644 index 000000000..7d39ff43e --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/expr.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifndef __cplusplus +#include +#endif + +struct file { + struct file *next; + struct file *parent; + char *name; + int lineno; + int flags; +}; + +#define FILE_BUSY 0x0001 +#define FILE_SCANNED 0x0002 +#define FILE_PRINTED 0x0004 + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define E_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define E_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define E_NOT(dep) (2-(dep)) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +struct symbol { + struct symbol *next; + char *name; + char *help; + enum symbol_type type; + struct symbol_value curr, user; + tristate visible; + int flags; + struct property *prop; + struct expr *dep, *dep2; + struct expr_value rev_dep; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_YES 0x0001 +#define SYMBOL_MOD 0x0002 +#define SYMBOL_NO 0x0004 +#define SYMBOL_CONST 0x0007 +#define SYMBOL_CHECK 0x0008 +#define SYMBOL_CHOICE 0x0010 +#define SYMBOL_CHOICEVAL 0x0020 +#define SYMBOL_PRINTED 0x0040 +#define SYMBOL_VALID 0x0080 +#define SYMBOL_OPTIONAL 0x0100 +#define SYMBOL_WRITE 0x0200 +#define SYMBOL_CHANGED 0x0400 +#define SYMBOL_NEW 0x0800 +#define SYMBOL_AUTO 0x1000 +#define SYMBOL_CHECKED 0x2000 +#define SYMBOL_CHECK_DONE 0x4000 +#define SYMBOL_WARNED 0x8000 + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 257 +#define SYMBOL_HASHMASK 0xff + +enum prop_type { + P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE +}; + +struct property { + struct property *next; + struct symbol *sym; + enum prop_type type; + const char *text; + struct expr_value visible; + struct expr *expr; + struct menu *menu; + struct file *file; + int lineno; +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *dep; + unsigned int flags; + //char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +#ifndef SWIG + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(struct expr *org); +void expr_free(struct expr *e); +int expr_eq(struct expr *e1, struct expr *e2); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_eliminate_yn(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); + +void expr_fprint(struct expr *e, FILE *out); +struct gstr; /* forward */ +void expr_gstr_print(struct expr *e, struct gstr *gs); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */ diff --git a/libs/nixio/axTLS/config/scripts/config/lex.zconf.c_shipped b/libs/nixio/axTLS/config/scripts/config/lex.zconf.c_shipped new file mode 100644 index 000000000..b877bb6b3 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lex.zconf.c_shipped @@ -0,0 +1,3688 @@ + +#line 3 "lex.zconf.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 31 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][38] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 18, 18, 19, 20, + 21, 22, 18, 18, 23, 24, 18, 25, 18, 26, + 27, 18, 28, 29, 30, 18, 18, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 18, 18, 19, 20, + 21, 22, 18, 18, 23, 24, 18, 25, 18, 26, + 27, 18, 28, 29, 30, 18, 18, 16 + + }, + + { + 11, 31, 32, 33, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31 + }, + + { + 11, 31, 32, 33, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31 + }, + + { + 11, 34, 34, 35, 34, 36, 34, 34, 36, 34, + 34, 34, 34, 34, 34, 37, 34, 34, 34, 34, + + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34 + }, + + { + 11, 34, 34, 35, 34, 36, 34, 34, 36, 34, + 34, 34, 34, 34, 34, 37, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34 + }, + + { + 11, 38, 38, 39, 40, 41, 42, 43, 41, 44, + 45, 46, 47, 47, 48, 49, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 50, 47, 47, 47, 51, + 47, 47, 47, 47, 47, 47, 47, 52 + + }, + + { + 11, 38, 38, 39, 40, 41, 42, 43, 41, 44, + 45, 46, 47, 47, 48, 49, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 50, 47, 47, 47, 51, + 47, 47, 47, 47, 47, 47, 47, 52 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 53, 54, -13, -13, 55, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, 58, -18, -18, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -18 + }, + + { + 11, -19, -19, -19, -19, -19, -19, -19, -19, -19, + -19, -19, -19, 58, -19, -19, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, + 58, 58, 58, 58, 58, 58, 58, -19 + + }, + + { + 11, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, 58, -20, -20, 58, 58, 58, 58, + 58, 58, 58, 58, 60, 58, 58, 58, 58, 61, + 58, 58, 58, 58, 58, 58, 58, -20 + }, + + { + 11, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, 58, -21, -21, 58, 58, 58, 58, + 58, 62, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -21 + }, + + { + 11, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, 58, -22, -22, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 63, 58, + 58, 58, 58, 58, 58, 58, 58, -22 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, 58, -23, -23, 58, 58, 58, 58, + 58, 64, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, 58, -24, -24, 58, 58, 58, 58, + 58, 58, 65, 58, 58, 58, 58, 58, 66, 58, + 58, 58, 58, 58, 58, 58, 58, -24 + + }, + + { + 11, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, 58, -25, -25, 58, 67, 58, 58, + 58, 68, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -25 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, 58, -26, -26, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 69, 58, 58, 58, 58, 58, 58, -26 + }, + + { + 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, 58, -27, -27, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 70, 58, 58, 58, 58, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, 58, -28, -28, 58, 71, 58, 58, + 58, 72, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, 58, -29, -29, 58, 58, 58, 58, + 58, 73, 58, 58, 58, 58, 58, 58, 58, 74, + 58, 58, 58, 58, 75, 58, 58, -29 + + }, + + { + 11, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, 58, -30, -30, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 76, 58, 58, 58, 58, -30 + }, + + { + 11, 77, 77, -31, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77 + }, + + { + 11, -32, 78, 79, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, 80, -33, -33, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80 + }, + + { + 11, 81, 81, 82, 81, -34, 81, 81, -34, 81, + 81, 81, 81, 81, 81, -34, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36 + }, + + { + 11, 83, 83, 84, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38 + }, + + { + 11, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, 85, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40 + }, + + { + 11, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41 + }, + + { + 11, 86, 86, -42, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86 + }, + + { + 11, -43, -43, -43, -43, -43, -43, 87, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43, -43 + }, + + { + 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44 + + }, + + { + 11, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45 + }, + + { + 11, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, 88, 89, 89, -46, -46, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -46 + }, + + { + 11, -47, -47, -47, -47, -47, -47, -47, -47, -47, + -47, 89, 89, 89, -47, -47, 89, 89, 89, 89, + + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -47 + }, + + { + 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, -49, -49, 90, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, 89, 89, 89, -50, -50, 89, 89, 89, 89, + 89, 89, 91, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -50 + }, + + { + 11, -51, -51, -51, -51, -51, -51, -51, -51, -51, + -51, 89, 89, 89, -51, -51, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 92, 89, + 89, 89, 89, 89, 89, 89, 89, -51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, 93 + }, + + { + 11, -53, 53, 54, -53, -53, 55, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54 + + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, 56, 56, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, 58, -58, -58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, 58, -59, -59, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, + 58, 58, 58, 58, 58, 58, 58, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, 58, -60, -60, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 95, + 58, 58, 58, 58, 58, 58, 58, -60 + }, + + { + 11, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, -61, 58, -61, -61, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 96, 97, 58, + 58, 58, 58, 58, 58, 58, 58, -61 + }, + + { + 11, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, 58, -62, -62, 58, 58, 58, 58, + + 58, 58, 98, 58, 58, 58, 58, 58, 58, 58, + 99, 58, 58, 58, 58, 58, 58, -62 + }, + + { + 11, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, 58, -63, -63, 58, 100, 58, 58, + 101, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -63 + }, + + { + 11, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, 58, -64, -64, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 102, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 103, -64 + + }, + + { + 11, -65, -65, -65, -65, -65, -65, -65, -65, -65, + -65, -65, -65, 58, -65, -65, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -65 + }, + + { + 11, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, 58, -66, -66, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 104, 58, 58, -66 + }, + + { + 11, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, 58, -67, -67, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 105, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -67 + }, + + { + 11, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, 58, -68, -68, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 106, 58, + 58, 58, 58, 58, 58, 58, 58, -68 + }, + + { + 11, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, 58, -69, -69, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 107, 58, 58, -69 + + }, + + { + 11, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, 58, -70, -70, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 108, + 58, 58, 58, 58, 58, 58, 58, -70 + }, + + { + 11, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, 58, -71, -71, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 109, 58, + 58, 58, 58, 58, 58, 58, 58, -71 + }, + + { + 11, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, 58, -72, -72, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 110, 58, 58, 58, 58, 58, -72 + }, + + { + 11, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, 58, -73, -73, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 111, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -73 + }, + + { + 11, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, 58, -74, -74, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 112, 58, -74 + + }, + + { + 11, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, 58, -75, -75, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 113, 58, 58, 58, 58, -75 + }, + + { + 11, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, 58, -76, -76, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 114, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -76 + }, + + { + 11, 77, 77, -77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77 + }, + + { + 11, -78, 78, 79, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78 + }, + + { + 11, 80, -79, -79, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80 + + }, + + { + 11, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80 + }, + + { + 11, 81, 81, 82, 81, -81, 81, 81, -81, 81, + 81, 81, 81, 81, 81, -81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81 + }, + + { + 11, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82 + }, + + { + 11, -83, -83, 84, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83 + }, + + { + 11, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84 + + }, + + { + 11, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85 + }, + + { + 11, 86, 86, -86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86 + }, + + { + 11, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87 + }, + + { + 11, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, 115, 89, 89, -88, -88, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -88 + }, + + { + 11, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, 89, 89, 89, -89, -89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -89 + + }, + + { + 11, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90 + }, + + { + 11, -91, -91, -91, -91, -91, -91, -91, -91, -91, + -91, 89, 89, 89, -91, -91, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -91 + }, + + { + 11, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, 89, 89, 89, -92, -92, 89, 89, 89, 89, + + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -92 + }, + + { + 11, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93 + }, + + { + 11, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, 58, -94, -94, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 116, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -94 + + }, + + { + 11, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, 58, -95, -95, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 117, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -95 + }, + + { + 11, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, 58, -96, -96, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 118, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -96 + }, + + { + 11, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, 58, -97, -97, 58, 58, 58, 58, + + 58, 58, 119, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -97 + }, + + { + 11, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, 58, -98, -98, 120, 121, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -98 + }, + + { + 11, -99, -99, -99, -99, -99, -99, -99, -99, -99, + -99, -99, -99, 58, -99, -99, 58, 58, 58, 58, + 58, 122, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -99 + + }, + + { + 11, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, 58, -100, -100, 58, 58, 123, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -100 + }, + + { + 11, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, 58, -101, -101, 58, 58, 58, 124, + 58, 58, 58, 58, 58, 125, 58, 126, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -101 + }, + + { + 11, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, 58, -102, -102, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 127, 58, 58, 58, 58, 58, 58, -102 + }, + + { + 11, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, 58, -103, -103, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -103 + }, + + { + 11, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, 58, -104, -104, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -104 + + }, + + { + 11, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, 58, -105, -105, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 128, 58, + 58, 58, 58, 58, 58, 58, 58, -105 + }, + + { + 11, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, 58, -106, -106, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 129, 58, -106 + }, + + { + 11, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 58, -107, -107, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 130, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -107 + }, + + { + 11, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, 58, -108, -108, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 131, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -108 + }, + + { + 11, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, 58, -109, -109, 58, 58, 58, 58, + 58, 58, 58, 132, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -109 + + }, + + { + 11, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, 58, -110, -110, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 133, 58, -110 + }, + + { + 11, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, 58, -111, -111, 58, 58, 58, 58, + 58, 134, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -111 + }, + + { + 11, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, 58, -112, -112, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 135, 58, 58, 58, 58, -112 + }, + + { + 11, -113, -113, -113, -113, -113, -113, -113, -113, -113, + -113, -113, -113, 58, -113, -113, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 136, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -113 + }, + + { + 11, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, 58, -114, -114, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 137, 58, 58, 58, -114 + + }, + + { + 11, -115, -115, -115, -115, -115, -115, -115, -115, -115, + -115, 89, 89, 89, -115, -115, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, -115 + }, + + { + 11, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, -116, 58, -116, -116, 58, 58, 58, 58, + 58, 138, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -116 + }, + + { + 11, -117, -117, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, 58, -117, -117, 58, 58, 58, 139, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -117 + }, + + { + 11, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, -118, 58, -118, -118, 58, 58, 58, 58, + 58, 140, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -118 + }, + + { + 11, -119, -119, -119, -119, -119, -119, -119, -119, -119, + -119, -119, -119, 58, -119, -119, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 141, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -119 + + }, + + { + 11, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, 58, -120, -120, 58, 58, 142, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 143, 58, 58, -120 + }, + + { + 11, -121, -121, -121, -121, -121, -121, -121, -121, -121, + -121, -121, -121, 58, -121, -121, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 144, 58, -121 + }, + + { + 11, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, 58, -122, -122, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 145, 58, + 58, 58, 58, 58, 58, 58, 58, -122 + }, + + { + 11, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, 58, -123, -123, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 146, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -123 + }, + + { + 11, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, 58, -124, -124, 58, 58, 58, 58, + 58, 58, 58, 58, 147, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -124 + + }, + + { + 11, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, 58, -125, -125, 58, 58, 58, 58, + 58, 58, 148, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -125 + }, + + { + 11, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, 58, -126, -126, 58, 58, 58, 58, + 58, 149, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -126 + }, + + { + 11, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, 58, -127, -127, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -127 + }, + + { + 11, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, 58, -128, -128, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 150, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -128 + }, + + { + 11, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, 58, -129, -129, 58, 58, 58, 151, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -129 + + }, + + { + 11, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, 58, -130, -130, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 152, + 58, 58, 58, 58, 58, 58, 58, -130 + }, + + { + 11, -131, -131, -131, -131, -131, -131, -131, -131, -131, + -131, -131, -131, 58, -131, -131, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 153, 58, 58, 58, 58, 58, 58, -131 + }, + + { + 11, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, 58, -132, -132, 58, 58, 58, 58, + + 58, 154, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -132 + }, + + { + 11, -133, -133, -133, -133, -133, -133, -133, -133, -133, + -133, -133, -133, 58, -133, -133, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 155, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -133 + }, + + { + 11, -134, -134, -134, -134, -134, -134, -134, -134, -134, + -134, -134, -134, 58, -134, -134, 58, 58, 58, 156, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -134 + + }, + + { + 11, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, 58, -135, -135, 58, 58, 58, 157, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -135 + }, + + { + 11, -136, -136, -136, -136, -136, -136, -136, -136, -136, + -136, -136, -136, 58, -136, -136, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 158, 58, + 58, 58, 58, 58, 58, 58, 58, -136 + }, + + { + 11, -137, -137, -137, -137, -137, -137, -137, -137, -137, + -137, -137, -137, 58, -137, -137, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 159, 58, 58, -137 + }, + + { + 11, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, 58, -138, -138, 58, 160, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -138 + }, + + { + 11, -139, -139, -139, -139, -139, -139, -139, -139, -139, + -139, -139, -139, 58, -139, -139, 58, 58, 58, 58, + 58, 161, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -139 + + }, + + { + 11, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, 58, -140, -140, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 162, 58, + 58, 58, 58, 58, 58, 58, 58, -140 + }, + + { + 11, -141, -141, -141, -141, -141, -141, -141, -141, -141, + -141, -141, -141, 58, -141, -141, 58, 58, 58, 58, + 58, 58, 58, 163, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -141 + }, + + { + 11, -142, -142, -142, -142, -142, -142, -142, -142, -142, + -142, -142, -142, 58, -142, -142, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 164, + 58, 58, 58, 58, 58, 58, 58, -142 + }, + + { + 11, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, 58, -143, -143, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 165, 58, 58, 58, 58, -143 + }, + + { + 11, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, 58, -144, -144, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 166, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -144 + + }, + + { + 11, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, 58, -145, -145, 58, 58, 58, 58, + 167, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -145 + }, + + { + 11, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, 58, -146, -146, 58, 58, 58, 58, + 58, 168, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -146 + }, + + { + 11, -147, -147, -147, -147, -147, -147, -147, -147, -147, + -147, -147, -147, 58, -147, -147, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 169, + 58, 58, 58, 58, 58, 58, 58, -147 + }, + + { + 11, -148, -148, -148, -148, -148, -148, -148, -148, -148, + -148, -148, -148, 58, -148, -148, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -148 + }, + + { + 11, -149, -149, -149, -149, -149, -149, -149, -149, -149, + -149, -149, -149, 58, -149, -149, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 170, 58, + 58, 58, 58, 58, 58, 58, 58, -149 + + }, + + { + 11, -150, -150, -150, -150, -150, -150, -150, -150, -150, + -150, -150, -150, 58, -150, -150, 58, 58, 58, 58, + 58, 171, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -150 + }, + + { + 11, -151, -151, -151, -151, -151, -151, -151, -151, -151, + -151, -151, -151, 58, -151, -151, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 172, + 58, 58, 58, 58, 58, 58, 58, -151 + }, + + { + 11, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, 58, -152, -152, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 173, 58, + 58, 58, 58, 58, 58, 58, 58, -152 + }, + + { + 11, -153, -153, -153, -153, -153, -153, -153, -153, -153, + -153, -153, -153, 58, -153, -153, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 174, 58, 58, -153 + }, + + { + 11, -154, -154, -154, -154, -154, -154, -154, -154, -154, + -154, -154, -154, 58, -154, -154, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -154 + + }, + + { + 11, -155, -155, -155, -155, -155, -155, -155, -155, -155, + -155, -155, -155, 58, -155, -155, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 175, 58, 58, 58, 58, -155 + }, + + { + 11, -156, -156, -156, -156, -156, -156, -156, -156, -156, + -156, -156, -156, 58, -156, -156, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 176, 58, 58, -156 + }, + + { + 11, -157, -157, -157, -157, -157, -157, -157, -157, -157, + -157, -157, -157, 58, -157, -157, 58, 58, 58, 58, + + 58, 177, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -157 + }, + + { + 11, -158, -158, -158, -158, -158, -158, -158, -158, -158, + -158, -158, -158, 58, -158, -158, 58, 58, 58, 58, + 58, 58, 58, 178, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -158 + }, + + { + 11, -159, -159, -159, -159, -159, -159, -159, -159, -159, + -159, -159, -159, 58, -159, -159, 58, 179, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -159 + + }, + + { + 11, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, 58, -160, -160, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 180, 58, + 58, 58, 58, 58, 58, 58, 58, -160 + }, + + { + 11, -161, -161, -161, -161, -161, -161, -161, -161, -161, + -161, -161, -161, 58, -161, -161, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -161 + }, + + { + 11, -162, -162, -162, -162, -162, -162, -162, -162, -162, + -162, -162, -162, 58, -162, -162, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 181, 58, 58, -162 + }, + + { + 11, -163, -163, -163, -163, -163, -163, -163, -163, -163, + -163, -163, -163, 58, -163, -163, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -163 + }, + + { + 11, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, 58, -164, -164, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 182, + 58, 58, 58, 58, 58, 58, 58, -164 + + }, + + { + 11, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, 58, -165, -165, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 183, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -165 + }, + + { + 11, -166, -166, -166, -166, -166, -166, -166, -166, -166, + -166, -166, -166, 58, -166, -166, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 184, 58, 58, -166 + }, + + { + 11, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, 58, -167, -167, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 185, 58, 58, 58, -167 + }, + + { + 11, -168, -168, -168, -168, -168, -168, -168, -168, -168, + -168, -168, -168, 58, -168, -168, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -168 + }, + + { + 11, -169, -169, -169, -169, -169, -169, -169, -169, -169, + -169, -169, -169, 58, -169, -169, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 186, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -169 + + }, + + { + 11, -170, -170, -170, -170, -170, -170, -170, -170, -170, + -170, -170, -170, 58, -170, -170, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 187, 58, -170 + }, + + { + 11, -171, -171, -171, -171, -171, -171, -171, -171, -171, + -171, -171, -171, 58, -171, -171, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 188, 58, + 58, 58, 58, 58, 58, 58, 58, -171 + }, + + { + 11, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, 58, -172, -172, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 189, 58, + 58, 58, 58, 58, 58, 58, 58, -172 + }, + + { + 11, -173, -173, -173, -173, -173, -173, -173, -173, -173, + -173, -173, -173, 58, -173, -173, 58, 190, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -173 + }, + + { + 11, -174, -174, -174, -174, -174, -174, -174, -174, -174, + -174, -174, -174, 58, -174, -174, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -174 + + }, + + { + 11, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, 58, -175, -175, 58, 58, 58, 58, + 58, 191, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -175 + }, + + { + 11, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, 58, -176, -176, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -176 + }, + + { + 11, -177, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, 58, -177, -177, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -177 + }, + + { + 11, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, 58, -178, -178, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -178 + }, + + { + 11, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, 58, -179, -179, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 192, 58, 58, -179 + + }, + + { + 11, -180, -180, -180, -180, -180, -180, -180, -180, -180, + -180, -180, -180, 58, -180, -180, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -180 + }, + + { + 11, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, 58, -181, -181, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -181 + }, + + { + 11, -182, -182, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, 58, -182, -182, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 193, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -182 + }, + + { + 11, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, -183, 58, -183, -183, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 194, 58, 58, 58, -183 + }, + + { + 11, -184, -184, -184, -184, -184, -184, -184, -184, -184, + -184, -184, -184, 58, -184, -184, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -184 + + }, + + { + 11, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, 58, -185, -185, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -185 + }, + + { + 11, -186, -186, -186, -186, -186, -186, -186, -186, -186, + -186, -186, -186, 58, -186, -186, 58, 58, 58, 195, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -186 + }, + + { + 11, -187, -187, -187, -187, -187, -187, -187, -187, -187, + -187, -187, -187, 58, -187, -187, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -187 + }, + + { + 11, -188, -188, -188, -188, -188, -188, -188, -188, -188, + -188, -188, -188, 58, -188, -188, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 196, 58, -188 + }, + + { + 11, -189, -189, -189, -189, -189, -189, -189, -189, -189, + -189, -189, -189, 58, -189, -189, 58, 58, 58, 58, + 58, 58, 197, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -189 + + }, + + { + 11, -190, -190, -190, -190, -190, -190, -190, -190, -190, + -190, -190, -190, 58, -190, -190, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 198, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -190 + }, + + { + 11, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, 58, -191, -191, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 199, 58, 58, 58, -191 + }, + + { + 11, -192, -192, -192, -192, -192, -192, -192, -192, -192, + -192, -192, -192, 58, -192, -192, 58, 58, 58, 58, + + 58, 200, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -192 + }, + + { + 11, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, 58, -193, -193, 58, 58, 58, 58, + 58, 201, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -193 + }, + + { + 11, -194, -194, -194, -194, -194, -194, -194, -194, -194, + -194, -194, -194, 58, -194, -194, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 202, 58, 58, -194 + + }, + + { + 11, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, 58, -195, -195, 58, 58, 58, 58, + 58, 203, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -195 + }, + + { + 11, -196, -196, -196, -196, -196, -196, -196, -196, -196, + -196, -196, -196, 58, -196, -196, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -196 + }, + + { + 11, -197, -197, -197, -197, -197, -197, -197, -197, -197, + -197, -197, -197, 58, -197, -197, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 204, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -197 + }, + + { + 11, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, 58, -198, -198, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -198 + }, + + { + 11, -199, -199, -199, -199, -199, -199, -199, -199, -199, + -199, -199, -199, 58, -199, -199, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -199 + + }, + + { + 11, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, 58, -200, -200, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -200 + }, + + { + 11, -201, -201, -201, -201, -201, -201, -201, -201, -201, + -201, -201, -201, 58, -201, -201, 58, 205, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -201 + }, + + { + 11, -202, -202, -202, -202, -202, -202, -202, -202, -202, + -202, -202, -202, 58, -202, -202, 58, 206, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -202 + }, + + { + 11, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, 58, -203, -203, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -203 + }, + + { + 11, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, 58, -204, -204, 58, 58, 58, 58, + 58, 58, 58, 207, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -204 + + }, + + { + 11, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, 58, -205, -205, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 208, 58, + 58, 58, 58, 58, 58, 58, 58, -205 + }, + + { + 11, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, 58, -206, -206, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 209, 58, 58, -206 + }, + + { + 11, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, 58, -207, -207, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -207 + }, + + { + 11, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, 58, -208, -208, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -208 + }, + + { + 11, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, 58, -209, -209, 58, 58, 58, 58, + 58, 210, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -209 + + }, + + { + 11, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, 58, -210, -210, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, -210 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 64 +#define YY_END_OF_BUFFER 65 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[211] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 65, 5, 4, 3, 2, 36, 37, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 63, 60, 62, 55, 59, 58, 57, 53, 48, 42, + 47, 51, 53, 40, 41, 50, 50, 43, 53, 50, + 50, 53, 4, 3, 2, 2, 1, 35, 35, 35, + 35, 35, 35, 35, 16, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 63, 60, 62, 61, + 55, 54, 57, 56, 44, 51, 38, 50, 50, 52, + 45, 46, 39, 35, 35, 35, 35, 35, 35, 35, + + 35, 35, 30, 29, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 49, 25, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 15, 35, 7, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 17, 35, 35, + 35, 35, 35, 34, 35, 35, 35, 35, 35, 35, + 10, 35, 13, 35, 35, 35, 35, 33, 35, 35, + 35, 35, 35, 22, 35, 32, 9, 31, 35, 26, + 12, 35, 35, 21, 18, 35, 8, 35, 35, 35, + 35, 35, 27, 35, 35, 6, 35, 20, 19, 23, + + 35, 35, 11, 35, 35, 35, 14, 28, 35, 24 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 15, 1, 1, 16, 1, 17, 18, 19, 20, + + 21, 22, 23, 24, 25, 13, 13, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 13, 13, 36, + 13, 13, 1, 37, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +char *text; +static char *text_ptr; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static struct buffer *zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_ptr = text; + text_size = 0; + *text_ptr = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + text = realloc(text, new_size); + text_asize = new_size; + text_ptr = text + text_size; + } + memcpy(text_ptr, str, size); + text_ptr += size; + text_size += size; + *text_ptr = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( (yy_init) ) + { + (yy_init) = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 2: +YY_RULE_SETUP + + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +current_file->lineno++; return T_EOL; + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +BEGIN(PARAM); return T_MAINMENU; + YY_BREAK +case 7: +YY_RULE_SETUP +BEGIN(PARAM); return T_MENU; + YY_BREAK +case 8: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDMENU; + YY_BREAK +case 9: +YY_RULE_SETUP +BEGIN(PARAM); return T_SOURCE; + YY_BREAK +case 10: +YY_RULE_SETUP +BEGIN(PARAM); return T_CHOICE; + YY_BREAK +case 11: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDCHOICE; + YY_BREAK +case 12: +YY_RULE_SETUP +BEGIN(PARAM); return T_COMMENT; + YY_BREAK +case 13: +YY_RULE_SETUP +BEGIN(PARAM); return T_CONFIG; + YY_BREAK +case 14: +YY_RULE_SETUP +BEGIN(PARAM); return T_MENUCONFIG; + YY_BREAK +case 15: +YY_RULE_SETUP +BEGIN(PARAM); return T_HELP; + YY_BREAK +case 16: +YY_RULE_SETUP +BEGIN(PARAM); return T_IF; + YY_BREAK +case 17: +YY_RULE_SETUP +BEGIN(PARAM); return T_ENDIF; + YY_BREAK +case 18: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEPENDS; + YY_BREAK +case 19: +YY_RULE_SETUP +BEGIN(PARAM); return T_REQUIRES; + YY_BREAK +case 20: +YY_RULE_SETUP +BEGIN(PARAM); return T_OPTIONAL; + YY_BREAK +case 21: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEFAULT; + YY_BREAK +case 22: +YY_RULE_SETUP +BEGIN(PARAM); return T_PROMPT; + YY_BREAK +case 23: +YY_RULE_SETUP +BEGIN(PARAM); return T_TRISTATE; + YY_BREAK +case 24: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_TRISTATE; + YY_BREAK +case 25: +YY_RULE_SETUP +BEGIN(PARAM); return T_BOOLEAN; + YY_BREAK +case 26: +YY_RULE_SETUP +BEGIN(PARAM); return T_BOOLEAN; + YY_BREAK +case 27: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_BOOLEAN; + YY_BREAK +case 28: +YY_RULE_SETUP +BEGIN(PARAM); return T_DEF_BOOLEAN; + YY_BREAK +case 29: +YY_RULE_SETUP +BEGIN(PARAM); return T_INT; + YY_BREAK +case 30: +YY_RULE_SETUP +BEGIN(PARAM); return T_HEX; + YY_BREAK +case 31: +YY_RULE_SETUP +BEGIN(PARAM); return T_STRING; + YY_BREAK +case 32: +YY_RULE_SETUP +BEGIN(PARAM); return T_SELECT; + YY_BREAK +case 33: +YY_RULE_SETUP +BEGIN(PARAM); return T_SELECT; + YY_BREAK +case 34: +YY_RULE_SETUP +BEGIN(PARAM); return T_RANGE; + YY_BREAK +case 35: +YY_RULE_SETUP +{ + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 36: +YY_RULE_SETUP + + YY_BREAK +case 37: +/* rule 37 can match eol */ +YY_RULE_SETUP +current_file->lineno++; BEGIN(INITIAL); + YY_BREAK + +case 38: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 39: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 40: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 41: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 42: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 43: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 44: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 45: +YY_RULE_SETUP +return T_IF; + YY_BREAK +case 46: +YY_RULE_SETUP +return T_ON; + YY_BREAK +case 47: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 48: +/* rule 48 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 49: +YY_RULE_SETUP +/* ignore */ + YY_BREAK +case 50: +YY_RULE_SETUP +{ + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 51: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 52: +/* rule 52 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 53: +YY_RULE_SETUP + + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 54: +/* rule 54 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 55: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 56: +/* rule 56 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 57: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 58: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 59: +/* rule 59 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 60: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 61: +/* rule 61 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 62: +/* rule 62 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 63: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_buf) { + zconf_endfile(); + return T_EOF; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 64: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * str ) +{ + + return zconf_scan_bytes(str,strlen(str) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef yytext_ptr +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static struct buffer *zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; + + return parent; +} + +int zconf_lineno(void) +{ + if (current_buf) + return current_file->lineno - 1; + else + return 0; +} + +char *zconf_curname(void) +{ + if (current_buf) + return current_file->name; + else + return ""; +} + diff --git a/libs/nixio/axTLS/config/scripts/config/lkc.h b/libs/nixio/axTLS/config/scripts/config/lkc.h new file mode 100644 index 000000000..b8a67fc9d --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lkc.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LKC_DIRECT_LINK +#define P(name,type,arg) extern type name arg +#else +#include "lkc_defs.h" +#define P(name,type,arg) extern type (*name ## _p) arg +#endif +#include "lkc_proto.h" +#undef P + +#define SRCTREE "srctree" + +int zconfparse(void); +void zconfdump(FILE *out); + +extern int zconfdebug; +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +char *zconf_curname(void); + +/* confdata.c */ +extern const char conf_def_filename[]; +extern char conf_filename[]; + +char *conf_get_default_confname(void); + +/* kconfig_load.c */ +void kconfig_load(void); + +/* menu.c */ +void menu_init(void); +void menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); +void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); + +/* util.c */ +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); + +struct gstr { + size_t len; + char *s; +}; +struct gstr str_new(void); +struct gstr str_assign(const char *s); +void str_free(struct gstr *gs); +void str_append(struct gstr *gs, const char *s); +void str_printf(struct gstr *gs, const char *fmt, ...); +const char *str_get(struct gstr *gs); + +/* symbol.c */ +void sym_init(void); +void sym_clear_all_valid(void); +void sym_set_changed(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_NEW ? false : true; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */ diff --git a/libs/nixio/axTLS/config/scripts/config/lkc_proto.h b/libs/nixio/axTLS/config/scripts/config/lkc_proto.h new file mode 100644 index 000000000..6dc6d0c48 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lkc_proto.h @@ -0,0 +1,40 @@ + +/* confdata.c */ +P(conf_parse,void,(const char *name)); +P(conf_read,int,(const char *name)); +P(conf_write,int,(const char *name)); + +/* menu.c */ +P(rootmenu,struct menu,); + +P(menu_is_visible,bool,(struct menu *menu)); +P(menu_get_prompt,const char *,(struct menu *menu)); +P(menu_get_root_menu,struct menu *,(struct menu *menu)); +P(menu_get_parent_menu,struct menu *,(struct menu *menu)); + +/* symbol.c */ +P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); +P(sym_change_count,int,); + +P(sym_lookup,struct symbol *,(const char *name, int isconst)); +P(sym_find,struct symbol *,(const char *name)); +P(sym_re_search,struct symbol **,(const char *pattern)); +P(sym_type_name,const char *,(enum symbol_type type)); +P(sym_calc_value,void,(struct symbol *sym)); +P(sym_get_type,enum symbol_type,(struct symbol *sym)); +P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); +P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); +P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); +P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); +P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); +P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); +P(sym_is_changable,bool,(struct symbol *sym)); +P(sym_get_choice_prop,struct property *,(struct symbol *sym)); +P(sym_get_default_prop,struct property *,(struct symbol *sym)); +P(sym_get_string_value,const char *,(struct symbol *sym)); + +P(prop_get_type_name,const char *,(enum prop_type type)); + +/* expr.c */ +P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)); diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/BIG.FAT.WARNING b/libs/nixio/axTLS/config/scripts/config/lxdialog/BIG.FAT.WARNING new file mode 100644 index 000000000..a8999d82b --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/BIG.FAT.WARNING @@ -0,0 +1,4 @@ +This is NOT the official version of dialog. This version has been +significantly modified from the original. It is for use by the Linux +kernel configuration script. Please do not bother Savio Lam with +questions about this program. diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/checklist.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/checklist.c new file mode 100644 index 000000000..71de4a191 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/checklist.c @@ -0,0 +1,372 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static int list_width, check_x, item_x, checkflag; + +/* + * Print list item + */ +static void +print_item (WINDOW * win, const char *item, int status, + int choice, int selected) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); + for (i = 0; i < list_width; i++) + waddch (win, ' '); + + wmove (win, choice, check_x); + wattrset (win, selected ? check_selected_attr : check_attr); + if (checkflag == FLAG_CHECK) + wprintw (win, "[%c]", status ? 'X' : ' '); + else + wprintw (win, "(%c)", status ? 'X' : ' '); + + wattrset (win, selected ? tag_selected_attr : tag_attr); + mvwaddch(win, choice, item_x, item[0]); + wattrset (win, selected ? item_selected_attr : item_attr); + waddstr (win, (char *)item+1); + if (selected) { + wmove (win, choice, check_x+1); + wrefresh (win); + } +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void +print_buttons( WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, "Select", y, x, selected == 0); + print_button (dialog, " Help ", y, x + 14, selected == 1); + + wmove(dialog, y, x+1 + 14*selected); + wrefresh (dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * The `flag' parameter is used to select between radiolist and checklist. + */ +int +dialog_checklist (const char *title, const char *prompt, int height, int width, + int list_height, int item_no, struct dialog_list_item ** items, + int flag) + +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status; + WINDOW *dialog, *list; + + checkflag = flag; + + /* Allocate space for storing item on/off status */ + if ((status = malloc (sizeof (int) * item_no)) == NULL) { + endwin (); + fprintf (stderr, + "\nCan't allocate memory in dialog_checklist().\n"); + exit (-1); + } + + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = (items[i]->selected == 1); /* ON */ + if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */ + choice = i + 1; + } + if (choice) + choice--; + + max_choice = MIN (list_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1); + + keypad (list, TRUE); + + /* draw a box around the list items */ + draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2, + menubox_border_attr, menubox_attr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + for (i = 0; i < item_no; i++) + check_x = MAX (check_x, + strlen (items[i]->name) + 4); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + if (choice >= list_height) { + scroll = choice - list_height + 1; + choice -= scroll; + } + + /* Print the list */ + for (i = 0; i < max_choice; i++) { + print_item (list, items[scroll + i]->name, + status[i+scroll], i, i == choice); + } + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + wnoutrefresh (list); + wnoutrefresh (dialog); + doupdate (); + + while (key != ESC) { + key = wgetch (dialog); + + for (i = 0; i < max_choice; i++) + if (toupper(key) == toupper(items[scroll + i]->name[0])) + break; + + + if ( i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-' ) { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + print_item (list, items[scroll]->name, + status[scroll], 0, FALSE); + scrollok (list, TRUE); + wscrl (list, -1); + scrollok (list, FALSE); + } + scroll--; + print_item (list, items[scroll]->name, + status[scroll], 0, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_no - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item (list, items[scroll + max_choice - 1]->name, + status[scroll + max_choice - 1], + max_choice - 1, FALSE); + scrollok (list, TRUE); + scroll (list); + scrollok (list, FALSE); + } + scroll++; + print_item (list, items[scroll + max_choice - 1]->name, + status[scroll + max_choice - 1], + max_choice - 1, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + print_item (list, items[scroll + choice]->name, + status[scroll + choice], choice, FALSE); + /* Highlight new item */ + choice = i; + print_item (list, items[scroll + choice]->name, + status[scroll + choice], choice, TRUE); + wnoutrefresh (list); + wrefresh (dialog); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + for (i = 0; i < item_no; i++) + items[i]->selected = 0; + items[scroll + choice]->selected = 1; + delwin (dialog); + free (status); + return 1; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case 'S': + case 's': + case ' ': + case '\n': + if (!button) { + if (flag == FLAG_CHECK) { + status[scroll + choice] = !status[scroll + choice]; + wmove (list, choice, check_x); + wattrset (list, check_selected_attr); + wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' '); + } else { + if (!status[scroll + choice]) { + for (i = 0; i < item_no; i++) + status[i] = 0; + status[scroll + choice] = 1; + for (i = 0; i < max_choice; i++) + print_item (list, items[scroll + i]->name, + status[scroll + i], i, i == choice); + } + } + wnoutrefresh (list); + wrefresh (dialog); + + for (i = 0; i < item_no; i++) { + items[i]->selected = status[i]; + } + } else { + for (i = 0; i < item_no; i++) + items[i]->selected = 0; + items[scroll + choice]->selected = 1; + } + delwin (dialog); + free (status); + return button; + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + + /* Now, update everything... */ + doupdate (); + } + + + delwin (dialog); + free (status); + return -1; /* ESC pressed */ +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/colors.h b/libs/nixio/axTLS/config/scripts/config/lxdialog/colors.h new file mode 100644 index 000000000..d34dd37c6 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/colors.h @@ -0,0 +1,161 @@ +/* + * colors.h -- color attribute definitions + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * Default color definitions + * + * *_FG = foreground + * *_BG = background + * *_HL = highlight? + */ +#define SCREEN_FG COLOR_CYAN +#define SCREEN_BG COLOR_BLUE +#define SCREEN_HL TRUE + +#define SHADOW_FG COLOR_BLACK +#define SHADOW_BG COLOR_BLACK +#define SHADOW_HL TRUE + +#define DIALOG_FG COLOR_BLACK +#define DIALOG_BG COLOR_WHITE +#define DIALOG_HL FALSE + +#define TITLE_FG COLOR_YELLOW +#define TITLE_BG COLOR_WHITE +#define TITLE_HL TRUE + +#define BORDER_FG COLOR_WHITE +#define BORDER_BG COLOR_WHITE +#define BORDER_HL TRUE + +#define BUTTON_ACTIVE_FG COLOR_WHITE +#define BUTTON_ACTIVE_BG COLOR_BLUE +#define BUTTON_ACTIVE_HL TRUE + +#define BUTTON_INACTIVE_FG COLOR_BLACK +#define BUTTON_INACTIVE_BG COLOR_WHITE +#define BUTTON_INACTIVE_HL FALSE + +#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE +#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE +#define BUTTON_KEY_ACTIVE_HL TRUE + +#define BUTTON_KEY_INACTIVE_FG COLOR_RED +#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE +#define BUTTON_KEY_INACTIVE_HL FALSE + +#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW +#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE +#define BUTTON_LABEL_ACTIVE_HL TRUE + +#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK +#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE +#define BUTTON_LABEL_INACTIVE_HL TRUE + +#define INPUTBOX_FG COLOR_BLACK +#define INPUTBOX_BG COLOR_WHITE +#define INPUTBOX_HL FALSE + +#define INPUTBOX_BORDER_FG COLOR_BLACK +#define INPUTBOX_BORDER_BG COLOR_WHITE +#define INPUTBOX_BORDER_HL FALSE + +#define SEARCHBOX_FG COLOR_BLACK +#define SEARCHBOX_BG COLOR_WHITE +#define SEARCHBOX_HL FALSE + +#define SEARCHBOX_TITLE_FG COLOR_YELLOW +#define SEARCHBOX_TITLE_BG COLOR_WHITE +#define SEARCHBOX_TITLE_HL TRUE + +#define SEARCHBOX_BORDER_FG COLOR_WHITE +#define SEARCHBOX_BORDER_BG COLOR_WHITE +#define SEARCHBOX_BORDER_HL TRUE + +#define POSITION_INDICATOR_FG COLOR_YELLOW +#define POSITION_INDICATOR_BG COLOR_WHITE +#define POSITION_INDICATOR_HL TRUE + +#define MENUBOX_FG COLOR_BLACK +#define MENUBOX_BG COLOR_WHITE +#define MENUBOX_HL FALSE + +#define MENUBOX_BORDER_FG COLOR_WHITE +#define MENUBOX_BORDER_BG COLOR_WHITE +#define MENUBOX_BORDER_HL TRUE + +#define ITEM_FG COLOR_BLACK +#define ITEM_BG COLOR_WHITE +#define ITEM_HL FALSE + +#define ITEM_SELECTED_FG COLOR_WHITE +#define ITEM_SELECTED_BG COLOR_BLUE +#define ITEM_SELECTED_HL TRUE + +#define TAG_FG COLOR_YELLOW +#define TAG_BG COLOR_WHITE +#define TAG_HL TRUE + +#define TAG_SELECTED_FG COLOR_YELLOW +#define TAG_SELECTED_BG COLOR_BLUE +#define TAG_SELECTED_HL TRUE + +#define TAG_KEY_FG COLOR_YELLOW +#define TAG_KEY_BG COLOR_WHITE +#define TAG_KEY_HL TRUE + +#define TAG_KEY_SELECTED_FG COLOR_YELLOW +#define TAG_KEY_SELECTED_BG COLOR_BLUE +#define TAG_KEY_SELECTED_HL TRUE + +#define CHECK_FG COLOR_BLACK +#define CHECK_BG COLOR_WHITE +#define CHECK_HL FALSE + +#define CHECK_SELECTED_FG COLOR_WHITE +#define CHECK_SELECTED_BG COLOR_BLUE +#define CHECK_SELECTED_HL TRUE + +#define UARROW_FG COLOR_GREEN +#define UARROW_BG COLOR_WHITE +#define UARROW_HL TRUE + +#define DARROW_FG COLOR_GREEN +#define DARROW_BG COLOR_WHITE +#define DARROW_HL TRUE + +/* End of default color definitions */ + +#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y))) +#define COLOR_NAME_LEN 10 +#define COLOR_COUNT 8 + +/* + * Global variables + */ + +typedef struct { + char name[COLOR_NAME_LEN]; + int value; +} color_names_st; + +extern color_names_st color_names[]; +extern int color_table[][3]; diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/dialog.h b/libs/nixio/axTLS/config/scripts/config/lxdialog/dialog.h new file mode 100644 index 000000000..7bab3ad0e --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/dialog.h @@ -0,0 +1,199 @@ + +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CURSES_LOC +#ifdef __sun__ +#define CURS_MACROS +#endif +#include CURSES_LOC + +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing*/ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + +#define ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* + * Attribute names + */ +#define screen_attr attributes[0] +#define shadow_attr attributes[1] +#define dialog_attr attributes[2] +#define title_attr attributes[3] +#define border_attr attributes[4] +#define button_active_attr attributes[5] +#define button_inactive_attr attributes[6] +#define button_key_active_attr attributes[7] +#define button_key_inactive_attr attributes[8] +#define button_label_active_attr attributes[9] +#define button_label_inactive_attr attributes[10] +#define inputbox_attr attributes[11] +#define inputbox_border_attr attributes[12] +#define searchbox_attr attributes[13] +#define searchbox_title_attr attributes[14] +#define searchbox_border_attr attributes[15] +#define position_indicator_attr attributes[16] +#define menubox_attr attributes[17] +#define menubox_border_attr attributes[18] +#define item_attr attributes[19] +#define item_selected_attr attributes[20] +#define tag_attr attributes[21] +#define tag_selected_attr attributes[22] +#define tag_key_attr attributes[23] +#define tag_key_selected_attr attributes[24] +#define check_attr attributes[25] +#define check_selected_attr attributes[26] +#define uarrow_attr attributes[27] +#define darrow_attr attributes[28] + +/* number of attributes */ +#define ATTRIBUTE_COUNT 29 + +/* + * Global variables + */ +extern bool use_colors; + +extern chtype attributes[]; +#endif + +extern const char *backtitle; + +struct dialog_list_item { + char *name; + int namelen; + char *tag; + int selected; /* Set to 1 by dialog_*() function. */ +}; + +/* + * Function prototypes + */ + +void init_dialog (void); +void end_dialog (void); +void dialog_clear (void); +#ifdef CURSES_LOC +void attr_clear (WINDOW * win, int height, int width, chtype attr); +void color_setup (void); +void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x); +void print_button (WINDOW * win, const char *label, int y, int x, int selected); +void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow (WINDOW * win, int y, int x, int height, int width); +#endif + +int first_alpha (const char *string, const char *exempt); +int dialog_yesno (const char *title, const char *prompt, int height, int width); +int dialog_msgbox (const char *title, const char *prompt, int height, + int width, int pause); +int dialog_textbox (const char *title, const char *file, int height, int width); +int dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *choice, int item_no, + struct dialog_list_item ** items); +int dialog_checklist (const char *title, const char *prompt, int height, + int width, int list_height, int item_no, + struct dialog_list_item ** items, int flag); +extern unsigned char dialog_input_result[]; +int dialog_inputbox (const char *title, const char *prompt, int height, + int width, const char *init); + +struct dialog_list_item *first_sel_item(int item_no, + struct dialog_list_item ** items); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#ifdef CURSES_LOC +#define M_EVENT (KEY_MAX+1) +#endif + + +/* + * The `flag' parameter in checklist is used to select between + * radiolist and checklist + */ +#define FLAG_CHECK 1 +#define FLAG_RADIO 0 diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/inputbox.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/inputbox.c new file mode 100644 index 000000000..fa7bebc69 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/inputbox.c @@ -0,0 +1,240 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +unsigned char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, " Ok ", y, x, selected==0); + print_button (dialog, " Help ", y, x + 14, selected==1); + + wmove(dialog, y, x+1+14*selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int +dialog_inputbox (const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, scroll = 0, key = 0, button = -1; + unsigned char *instr = dialog_input_result; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx (dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2, + border_attr, dialog_attr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove (dialog, box_y, box_x); + wattrset (dialog, inputbox_attr); + + if (!init) + instr[0] = '\0'; + else + strcpy (instr, init); + + input_x = strlen (instr); + + if (input_x >= box_width) { + scroll = input_x - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else + waddstr (dialog, instr); + + wmove (dialog, box_y, box_x + input_x); + + wrefresh (dialog); + + while (key != ESC) { + key = wgetch (dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_LEFT: + continue; + case KEY_RIGHT: + continue; + case KEY_BACKSPACE: + case 127: + if (input_x || scroll) { + wattrset (dialog, inputbox_attr); + if (!input_x) { + scroll = scroll < box_width - 1 ? + 0 : scroll - (box_width - 1); + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width; i++) + waddch (dialog, instr[scroll + input_x + i] ? + instr[scroll + input_x + i] : ' '); + input_x = strlen (instr) - scroll; + } else + input_x--; + instr[scroll + input_x] = '\0'; + mvwaddch (dialog, box_y, input_x + box_x, ' '); + wmove (dialog, box_y, input_x + box_x); + wrefresh (dialog); + } + continue; + default: + if (key < 0x100 && isprint (key)) { + if (scroll + input_x < MAX_LEN) { + wattrset (dialog, inputbox_attr); + instr[scroll + input_x] = key; + instr[scroll + input_x + 1] = '\0'; + if (input_x == box_width - 1) { + scroll++; + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else { + wmove (dialog, box_y, input_x++ + box_x); + waddch (dialog, key); + } + wrefresh (dialog); + } else + flash (); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin (dialog); + return 0; + case 'H': + case 'h': + delwin (dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + } + break; + case ' ': + case '\n': + delwin (dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/menubox.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/menubox.c new file mode 100644 index 000000000..873dc587b --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/menubox.c @@ -0,0 +1,438 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changes by Clifford Wolf (god@clifford.at) + * + * [ 1998-06-13 ] + * + * *) A bugfix for the Page-Down problem + * + * *) Formerly when I used Page Down and Page Up, the cursor would be set + * to the first position in the menu box. Now lxdialog is a bit + * smarter and works more like other menu systems (just have a look at + * it). + * + * *) Formerly if I selected something my scrolling would be broken because + * lxdialog is re-invoked by the Menuconfig shell script, can't + * remember the last scrolling position, and just sets it so that the + * cursor is at the bottom of the box. Now it writes the temporary file + * lxdialog.scrltmp which contains this information. The file is + * deleted by lxdialog if the user leaves a submenu or enters a new + * one, but it would be nice if Menuconfig could make another "rm -f" + * just to be sure. Just try it out - you will recognise a difference! + * + * [ 1998-06-14 ] + * + * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files + * and menus change their size on the fly. + * + * *) If for some reason the last scrolling position is not saved by + * lxdialog, it sets the scrolling so that the selected item is in the + * middle of the menu box, not at the bottom. + * + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. + * This fixes a bug in Menuconfig where using ' ' to descend into menus + * would leave mis-synchronized lxdialog.scrltmp files lying around, + * fscanf would read in 'scroll', and eventually that value would get used. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void +print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey) +{ + int j; + char menu_item[menu_width+1]; + + strncpy(menu_item, item, menu_width); + menu_item[menu_width] = 0; + j = first_alpha(menu_item, "YyNnMmHh"); + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); +#if OLD_NCURSES + { + int i; + for (i = 0; i < menu_width; i++) + waddch (win, ' '); + } +#else + wclrtoeol(win); +#endif + wattrset (win, selected ? item_selected_attr : item_attr); + mvwaddstr (win, choice, item_x, menu_item); + if (hotkey) { + wattrset (win, selected ? tag_key_selected_attr : tag_key_attr); + mvwaddch(win, choice, item_x+j, menu_item[j]); + } + if (selected) { + wmove (win, choice, item_x+1); + wrefresh (win); + } +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int item_no, int scroll, + int y, int x, int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); +} + +/* + * Display the termination buttons. + */ +static void +print_buttons (WINDOW *win, int height, int width, int selected) +{ + int x = width / 2 - 16; + int y = height - 2; + + print_button (win, "Select", y, x, selected == 0); + print_button (win, " Exit ", y, x + 12, selected == 1); + print_button (win, " Help ", y, x + 24, selected == 2); + + wmove(win, y, x+1+12*selected); + wrefresh (win); +} + +/* + * Display a menu for choosing among a number of options + */ +int +dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *current, int item_no, + struct dialog_list_item ** items) +{ + int i, j, x, y, box_x, box_y; + int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice; + WINDOW *dialog, *menu; + FILE *f; + + max_choice = MIN (menu_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin (dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad (menu, TRUE); + + /* draw a box around the menu items */ + draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2, + menubox_border_attr, menubox_attr); + + /* + * Find length of longest item in order to center menu. + * Set 'choice' to default item. + */ + item_x = 0; + for (i = 0; i < item_no; i++) { + item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2)); + if (strcmp(current, items[i]->tag) == 0) choice = i; + } + + item_x = (menu_width - item_x) / 2; + + /* get the scroll info from the temp file */ + if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) { + if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) && + (scroll+max_choice > choice) && (scroll >= 0) && + (scroll+max_choice <= item_no) ) { + first_item = scroll; + choice = choice - scroll; + fclose(f); + } else { + scroll=0; + remove("lxdialog.scrltmp"); + fclose(f); + f=NULL; + } + } + if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) { + if (choice >= item_no-max_choice/2) + scroll = first_item = item_no-max_choice; + else + scroll = first_item = choice - max_choice/2; + choice = choice - scroll; + } + + /* Print the menu */ + for (i=0; i < max_choice; i++) { + print_item (menu, items[first_item + i]->name, i, i == choice, + (items[first_item + i]->tag[0] != ':')); + } + + wnoutrefresh (menu); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + print_buttons (dialog, height, width, 0); + wmove (menu, choice, item_x+1); + wrefresh (menu); + + while (key != ESC) { + key = wgetch(menu); + + if (key < 256 && isalpha(key)) key = tolower(key); + + if (strchr("ynmh", key)) + i = max_choice; + else { + for (i = choice+1; i < max_choice; i++) { + j = first_alpha(items[scroll + i]->name, "YyNnMmHh"); + if (key == tolower(items[scroll + i]->name[j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + j = first_alpha(items[scroll + i]->name, "YyNnMmHh"); + if (key == tolower(items[scroll + i]->name[j])) + break; + } + } + + if (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE) { + + print_item (menu, items[scroll + choice]->name, choice, FALSE, + (items[scroll + choice]->tag[0] != ':')); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + scrollok (menu, TRUE); + wscrl (menu, -1); + scrollok (menu, FALSE); + + scroll--; + + print_item (menu, items[scroll]->name, 0, FALSE, + (items[scroll]->tag[0] != ':')); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + + print_item (menu, items[scroll + choice]->name, choice, FALSE, + (items[scroll + choice]->tag[0] != ':')); + + if ((choice > max_choice-3) && + (scroll + max_choice < item_no) + ) { + /* Scroll menu up */ + scrollok (menu, TRUE); + scroll (menu); + scrollok (menu, FALSE); + + scroll++; + + print_item (menu, items[scroll + max_choice - 1]->name, + max_choice-1, FALSE, + (items[scroll + max_choice - 1]->tag[0] != ':')); + } else + choice = MIN(choice+1, max_choice-1); + + } else if (key == KEY_PPAGE) { + scrollok (menu, TRUE); + for (i=0; (i < max_choice); i++) { + if (scroll > 0) { + wscrl (menu, -1); + scroll--; + print_item (menu, items[scroll]->name, 0, FALSE, + (items[scroll]->tag[0] != ':')); + } else { + if (choice > 0) + choice--; + } + } + scrollok (menu, FALSE); + + } else if (key == KEY_NPAGE) { + for (i=0; (i < max_choice); i++) { + if (scroll+max_choice < item_no) { + scrollok (menu, TRUE); + scroll(menu); + scrollok (menu, FALSE); + scroll++; + print_item (menu, items[scroll + max_choice - 1]->name, + max_choice-1, FALSE, + (items[scroll + max_choice - 1]->tag[0] != ':')); + } else { + if (choice+1 < max_choice) + choice++; + } + } + + } else + choice = i; + + print_item (menu, items[scroll + choice]->name, choice, TRUE, + (items[scroll + choice]->tag[0] != ':')); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + wnoutrefresh (dialog); + wrefresh (menu); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 2 : (button > 2 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (menu); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + case '/': + /* save scroll info */ + if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) { + fprintf(f,"%d\n",scroll); + fclose(f); + } + delwin (dialog); + items[scroll + choice]->selected = 1; + switch (key) { + case 's': return 3; + case 'y': return 3; + case 'n': return 4; + case 'm': return 5; + case ' ': return 6; + case '/': return 7; + } + return 0; + case 'h': + case '?': + button = 2; + case '\n': + delwin (dialog); + items[scroll + choice]->selected = 1; + + remove("lxdialog.scrltmp"); + return button; + case 'e': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + remove("lxdialog.scrltmp"); + return -1; /* ESC pressed */ +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/msgbox.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/msgbox.c new file mode 100644 index 000000000..93692e1fb --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/msgbox.c @@ -0,0 +1,85 @@ +/* + * msgbox.c -- implements the message box and info box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display a message box. Program will pause and display an "OK" button + * if the parameter 'pause' is non-zero. + */ +int +dialog_msgbox (const char *title, const char *prompt, int height, int width, + int pause) +{ + int i, x, y, key = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 2); + + if (pause) { + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + print_button (dialog, " Ok ", + height - 2, width / 2 - 4, TRUE); + + wrefresh (dialog); + while (key != ESC && key != '\n' && key != ' ' && + key != 'O' && key != 'o' && key != 'X' && key != 'x') + key = wgetch (dialog); + } else { + key = '\n'; + wrefresh (dialog); + } + + delwin (dialog); + return key == ESC ? -1 : 0; +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/textbox.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/textbox.c new file mode 100644 index 000000000..a5a460b5c --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/textbox.c @@ -0,0 +1,556 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static void back_lines (int n); +static void print_page (WINDOW * win, int height, int width); +static void print_line (WINDOW * win, int row, int width); +static char *get_line (void); +static void print_position (WINDOW * win, int height, int width); + +static int hscroll, fd, file_size, bytes_read; +static int begin_reached = 1, end_reached, page_length; +static char *buf, *page; + +/* + * Display text from a file in a dialog box. + */ +int +dialog_textbox (const char *title, const char *file, int height, int width) +{ + int i, x, y, cur_x, cur_y, fpos, key = 0; + int passed_end; + char search_term[MAX_LEN + 1]; + WINDOW *dialog, *text; + + search_term[0] = '\0'; /* no search term entered yet */ + + /* Open input file for reading */ + if ((fd = open (file, O_RDONLY)) == -1) { + endwin (); + fprintf (stderr, + "\nCan't open input file in dialog_textbox().\n"); + exit (-1); + } + /* Get file size. Actually, 'file_size' is the real file size - 1, + since it's only the last byte offset from the beginning */ + if ((file_size = lseek (fd, 0, SEEK_END)) == -1) { + endwin (); + fprintf (stderr, "\nError getting file size in dialog_textbox().\n"); + exit (-1); + } + /* Restore file pointer to beginning of file after getting file size */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + /* Allocate space for read buffer */ + if ((buf = malloc (BUF_SIZE + 1)) == NULL) { + endwin (); + fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; /* mark end of valid data */ + page = buf; /* page is pointer to start of page to be displayed */ + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + /* Create window for text region, used for scrolling text */ + text = subwin (dialog, height - 4, width - 2, y + 1, x + 1); + wattrset (text, dialog_attr); + wbkgdset (text, dialog_attr & A_COLOR); + + keypad (text, TRUE); + + /* register the new window, along with its borders */ + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE); + wnoutrefresh (dialog); + getyx (dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear (text, height - 4, width - 2, dialog_attr); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + + while ((key != ESC) && (key != '\n')) { + key = wgetch (dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + delwin (dialog); + free (buf); + close (fd); + return 0; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + /* First page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Yes, we have to read it in */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* Last page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos < file_size) { /* Yes, we have to read it in */ + if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf + bytes_read; + back_lines (height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (!begin_reached) { + back_lines (page_length + 1); + + /* We don't call print_page() here but use scrolling to ensure + faster screen update. However, 'end_reached' and + 'page_length' should still be updated, and 'page' should + point to start of next page. This is done by calling + get_line() in the following 'for' loop. */ + scrollok (text, TRUE); + wscrl (text, -1); /* Scroll text region down one line */ + scrollok (text, FALSE); + page_length = 0; + passed_end = 0; + for (i = 0; i < height - 4; i++) { + if (!i) { + /* print first line of page */ + print_line (text, 0, width - 2); + wnoutrefresh (text); + } else + /* Called to update 'end_reached' and 'page' */ + get_line (); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'B': /* Previous page */ + case 'b': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines (page_length + height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (!end_reached) { + begin_reached = 0; + scrollok (text, TRUE); + scroll (text); /* Scroll text region up one line */ + scrollok (text, FALSE); + print_line (text, height - 5, width - 2); + wnoutrefresh (text); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case KEY_NPAGE: /* Next page */ + case ' ': + if (end_reached) + break; + + begin_reached = 0; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case ESC: + break; + } + } + + delwin (dialog); + free (buf); + close (fd); + return 1; /* ESC pressed */ +} + +/* + * Go back 'n' lines in text file. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void +back_lines (int n) +{ + int i, fpos; + + begin_reached = 0; + /* We have to distinguish between end_reached and !end_reached + since at end of file, the line is not ended by a '\n'. + The code inside 'if' basically does a '--page' to move one + character backward so as to skip '\n' of the previous line */ + if (!end_reached) { + /* Either beginning of buffer or beginning of file reached? */ + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Not beginning of file yet */ + /* We've reached beginning of buffer, but not beginning of + file yet, so read previous part of file into buffer. + Note that we only move backward for BUF_SIZE/2 bytes, + but not BUF_SIZE bytes to avoid re-reading again in + print_page() later */ + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) + == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + if (*(--page) != '\n') { /* '--page' here */ + /* Something's wrong... */ + endwin (); + fprintf (stderr, "\nInternal error in back_lines().\n"); + exit (-1); + } + } + /* Go back 'n' lines */ + for (i = 0; i < n; i++) + do { + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), + SEEK_CUR) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer" + " in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in " + "back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + } while (*(--page) != '\n'); + page++; +} + +/* + * Print a new page of text. Called by dialog_textbox(). + */ +static void +print_page (WINDOW * win, int height, int width) +{ + int i, passed_end = 0; + + page_length = 0; + for (i = 0; i < height; i++) { + print_line (win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh (win); +} + +/* + * Print a new line of text. Called by dialog_textbox() and print_page(). + */ +static void +print_line (WINDOW * win, int row, int width) +{ + int y, x; + char *line; + + line = get_line (); + line += MIN (strlen (line), hscroll); /* Scroll horizontally */ + wmove (win, row, 0); /* move cursor to correct line */ + waddch (win, ' '); + waddnstr (win, line, MIN (strlen (line), width - 2)); + + getyx (win, y, x); + /* Clear 'residue' of previous line */ +#if OLD_NCURSES + { + int i; + for (i = 0; i < width - x; i++) + waddch (win, ' '); + } +#else + wclrtoeol(win); +#endif +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char * +get_line (void) +{ + int i = 0, fpos; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + /* Either end of file or end of buffer reached */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "get_line().\n"); + exit (-1); + } + if (fpos < file_size) { /* Not end of file yet */ + /* We've reached end of buffer, but not end of file yet, + so read next part of file into buffer */ + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in get_line().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + page = buf; + } else { + if (!end_reached) + end_reached = 1; + break; + } + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move pass '\n' */ + + return line; +} + +/* + * Print current position + */ +static void +print_position (WINDOW * win, int height, int width) +{ + int fpos, percent; + + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in print_position().\n"); + exit (-1); + } + wattrset (win, position_indicator_attr); + wbkgdset (win, position_indicator_attr & A_COLOR); + percent = !file_size ? + 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; + wmove (win, height - 3, width - 9); + wprintw (win, "(%3d%%)", percent); +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/util.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/util.c new file mode 100644 index 000000000..6f83951b9 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/util.c @@ -0,0 +1,375 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + + +/* use colors by default? */ +bool use_colors = 1; + +const char *backtitle = NULL; + +const char *dialog_result; + +/* + * Attribute values, default is for mono display + */ +chtype attributes[] = +{ + A_NORMAL, /* screen_attr */ + A_NORMAL, /* shadow_attr */ + A_NORMAL, /* dialog_attr */ + A_BOLD, /* title_attr */ + A_NORMAL, /* border_attr */ + A_REVERSE, /* button_active_attr */ + A_DIM, /* button_inactive_attr */ + A_REVERSE, /* button_key_active_attr */ + A_BOLD, /* button_key_inactive_attr */ + A_REVERSE, /* button_label_active_attr */ + A_NORMAL, /* button_label_inactive_attr */ + A_NORMAL, /* inputbox_attr */ + A_NORMAL, /* inputbox_border_attr */ + A_NORMAL, /* searchbox_attr */ + A_BOLD, /* searchbox_title_attr */ + A_NORMAL, /* searchbox_border_attr */ + A_BOLD, /* position_indicator_attr */ + A_NORMAL, /* menubox_attr */ + A_NORMAL, /* menubox_border_attr */ + A_NORMAL, /* item_attr */ + A_REVERSE, /* item_selected_attr */ + A_BOLD, /* tag_attr */ + A_REVERSE, /* tag_selected_attr */ + A_BOLD, /* tag_key_attr */ + A_REVERSE, /* tag_key_selected_attr */ + A_BOLD, /* check_attr */ + A_REVERSE, /* check_selected_attr */ + A_BOLD, /* uarrow_attr */ + A_BOLD /* darrow_attr */ +}; + + +#include "colors.h" + +/* + * Table of color values + */ +int color_table[][3] = +{ + {SCREEN_FG, SCREEN_BG, SCREEN_HL}, + {SHADOW_FG, SHADOW_BG, SHADOW_HL}, + {DIALOG_FG, DIALOG_BG, DIALOG_HL}, + {TITLE_FG, TITLE_BG, TITLE_HL}, + {BORDER_FG, BORDER_BG, BORDER_HL}, + {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL}, + {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL}, + {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL}, + {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL}, + {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL}, + {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG, + BUTTON_LABEL_INACTIVE_HL}, + {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL}, + {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL}, + {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL}, + {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL}, + {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL}, + {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL}, + {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL}, + {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL}, + {ITEM_FG, ITEM_BG, ITEM_HL}, + {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL}, + {TAG_FG, TAG_BG, TAG_HL}, + {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL}, + {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL}, + {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL}, + {CHECK_FG, CHECK_BG, CHECK_HL}, + {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL}, + {UARROW_FG, UARROW_BG, UARROW_HL}, + {DARROW_FG, DARROW_BG, DARROW_HL}, +}; /* color_table */ + +/* + * Set window to attribute 'attr' + */ +void +attr_clear (WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset (win, attr); + for (i = 0; i < height; i++) { + wmove (win, i, 0); + for (j = 0; j < width; j++) + waddch (win, ' '); + } + touchwin (win); +} + +void dialog_clear (void) +{ + attr_clear (stdscr, LINES, COLS, screen_attr); + /* Display background title if it exists ... - SLH */ + if (backtitle != NULL) { + int i; + + wattrset (stdscr, screen_attr); + mvwaddstr (stdscr, 0, 1, (char *)backtitle); + wmove (stdscr, 1, 1); + for (i = 1; i < COLS - 1; i++) + waddch (stdscr, ACS_HLINE); + } + wnoutrefresh (stdscr); +} + +/* + * Do some initialization for dialog + */ +void +init_dialog (void) +{ + initscr (); /* Init curses */ + keypad (stdscr, TRUE); + cbreak (); + noecho (); + + + if (use_colors) /* Set up colors */ + color_setup (); + + + dialog_clear (); +} + +/* + * Setup for color display + */ +void +color_setup (void) +{ + int i; + + if (has_colors ()) { /* Terminal supports color? */ + start_color (); + + /* Initialize color pairs */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + init_pair (i + 1, color_table[i][0], color_table[i][1]); + + /* Setup color attributes */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + attributes[i] = C_ATTR (color_table[i][2], i + 1); + } +} + +/* + * End using dialog functions. + */ +void +end_dialog (void) +{ + endwin (); +} + + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are replaced by spaces. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void +print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int i, prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2; + + strcpy (tempstr, prompt); + + prompt_len = strlen(tempstr); + + /* + * Remove newlines + */ + for(i=0; i room || + (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room + && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) { + cur_y++; + cur_x = x; + } + wmove (win, cur_y, cur_x); + waddstr (win, word); + getyx (win, cur_y, cur_x); + cur_x++; + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' '); + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void +print_button (WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove (win, y, x); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, "<"); + temp = strspn (label, " "); + label += temp; + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + for (i = 0; i < temp; i++) + waddch (win, ' '); + wattrset (win, selected ? button_key_active_attr + : button_key_inactive_attr); + waddch (win, label[0]); + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + waddstr (win, (char *)label + 1); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, ">"); + wmove (win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box (WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset (win, 0); + for (i = 0; i < height; i++) { + wmove (win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch (win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch (win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch (win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch (win, box | ACS_LRCORNER); + else if (!i) + waddch (win, border | ACS_HLINE); + else if (i == height - 1) + waddch (win, box | ACS_HLINE); + else if (!j) + waddch (win, border | ACS_VLINE); + else if (j == width - 1) + waddch (win, box | ACS_VLINE); + else + waddch (win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void +draw_shadow (WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors ()) { /* Whether terminal supports color? */ + wattrset (win, shadow_attr); + wmove (win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch (win, winch (win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove (win, i, x + width); + waddch (win, winch (win) & A_CHARTEXT); + waddch (win, winch (win) & A_CHARTEXT); + } + wnoutrefresh (win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int +first_alpha(const char *string, const char *exempt) +{ + int i, in_paren=0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) ++in_paren; + if (strchr(">])", c) && in_paren > 0) --in_paren; + + if ((! in_paren) && isalpha(c) && + strchr(exempt, c) == 0) + return i; + } + + return 0; +} + +/* + * Get the first selected item in the dialog_list_item list. + */ +struct dialog_list_item * +first_sel_item(int item_no, struct dialog_list_item ** items) +{ + int i; + + for (i = 0; i < item_no; i++) { + if (items[i]->selected) + return items[i]; + } + + return NULL; +} diff --git a/libs/nixio/axTLS/config/scripts/config/lxdialog/yesno.c b/libs/nixio/axTLS/config/scripts/config/lxdialog/yesno.c new file mode 100644 index 000000000..11fcc25f5 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/lxdialog/yesno.c @@ -0,0 +1,118 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button (dialog, " Yes ", y, x, selected == 0); + print_button (dialog, " No ", y, x + 13, selected == 1); + + wmove(dialog, y, x+1 + 13*selected ); + wrefresh (dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int +dialog_yesno (const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL && strlen(title) >= width-2 ) { + /* truncate long title -- mec */ + char * title2 = malloc(width-2+1); + memcpy( title2, title, width-2 ); + title2[width-2] = '\0'; + title = title2; + } + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != ESC) { + key = wgetch (dialog); + switch (key) { + case 'Y': + case 'y': + delwin (dialog); + return 0; + case 'N': + case 'n': + delwin (dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case ' ': + case '\n': + delwin (dialog); + return button; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/libs/nixio/axTLS/config/scripts/config/mconf.c b/libs/nixio/axTLS/config/scripts/config/mconf.c new file mode 100644 index 000000000..406eb29c3 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/mconf.c @@ -0,0 +1,977 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + * + * Introduced single menu mode (show all sub-menus in one large tree). + * 2002-11-06 Petr Baudis + * + * Directly use liblxdialog library routines. + * 2002-11-14 Petr Baudis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lxdialog/dialog.h" + +#define LKC_DIRECT_LINK +#include "lkc.h" + +static char menu_backtitle[128]; +static const char mconf_readme[] = +"Overview\n" +"--------\n" +"Some features may be built directly into axTLS. Some features\n" +"may be completely removed altogether. There are also certain\n" +"parameters which are not really features, but must be\n" +"entered in as decimal or hexadecimal numbers or possibly text.\n" +"\n" +"Menu items beginning with [*] or [ ] represent features\n" +"configured to be built in or removed respectively.\n" +"\n" +"To change any of these features, highlight it with the cursor\n" +"keys and press to build it in or to removed it.\n" +"You may also press the to cycle\n" +"through the available options (ie. Y->N->Y).\n" +"\n" +"Some additional keyboard hints:\n" +"\n" +"Menus\n" +"----------\n" +"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n" +" you wish to change or submenu wish to select and press .\n" +" Submenus are designated by \"--->\".\n" +"\n" +" Shortcut: Press the option's highlighted letter (hotkey).\n" +" Pressing a hotkey more than once will sequence\n" +" through all visible items which use that hotkey.\n" +"\n" +" You may also use the and keys to scroll\n" +" unseen options into view.\n" +"\n" +"o To exit a menu use the cursor keys to highlight the button\n" +" and press .\n" +"\n" +" Shortcut: Press or or if there is no hotkey\n" +" using those letters. You may press a single , but\n" +" there is a delayed response which you may find annoying.\n" +"\n" +" Also, the and cursor keys will cycle between and\n" +" \n" +"\n" +"\n" +"Data Entry\n" +"-----------\n" +"o Enter the requested information and press \n" +" If you are entering hexadecimal values, it is not necessary to\n" +" add the '0x' prefix to the entry.\n" +"\n" +"o For help, use the or cursor keys to highlight the help option\n" +" and press . You can try as well.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"--------\n" +"o Use the cursor keys to scroll up/down/left/right. The VI editor\n" +" keys h,j,k,l function here as do and for those\n" +" who are familiar with less and lynx.\n" +"\n" +"o Press , , or to exit.\n" +"\n" +"\n" +"Alternate Configuration Files\n" +"-----------------------------\n" +"Menuconfig supports the use of alternate configuration files for\n" +"those who, for various reasons, find it necessary to switch\n" +"between different configurations.\n" +"\n" +"At the end of the main menu you will find two options. One is\n" +"for saving the current configuration to a file of your choosing.\n" +"The other option is for loading a previously saved alternate\n" +"configuration.\n" +"\n" +"Even if you don't use alternate configuration files, but you\n" +"find during a Menuconfig session that you have completely messed\n" +"up your settings, you may use the \"Load Alternate...\" option to\n" +"restore your previously saved settings from \".config\" without\n" +"restarting Menuconfig.\n" +"\n" +"Other information\n" +"-----------------\n" +"If you use Menuconfig in an XTERM window make sure you have your\n" +"$TERM variable set to point to a xterm definition which supports color.\n" +"Otherwise, Menuconfig will look rather bad. Menuconfig will not\n" +"display correctly in a RXVT window because rxvt displays only one\n" +"intensity of color, bright.\n" +"\n" +"Menuconfig will display larger menus on screens or xterms which are\n" +"set to display more than the standard 25 row by 80 column geometry.\n" +"In order for this to work, the \"stty size\" command must be able to\n" +"display the screen's current row and column geometry. I STRONGLY\n" +"RECOMMEND that you make sure you do NOT have the shell variables\n" +"LINES and COLUMNS exported into your environment. Some distributions\n" +"export those variables via /etc/profile. Some ncurses programs can\n" +"become confused when those variables (LINES & COLUMNS) don't reflect\n" +"the true screen size.\n" +"\n" +"Optional personality available\n" +"------------------------------\n" +"If you prefer to have all of the options listed in a single\n" +"menu, rather than the default multimenu hierarchy, run the menuconfig\n" +"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make MENUCONFIG_MODE=single_menu menuconfig\n" +"\n" +" will then unroll the appropriate category, or enfold it if it\n" +"is already unrolled.\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive\n" +"(especially with a larger number of unrolled categories) than the\n" +"default mode.\n", +menu_instructions[] = + "Arrow keys navigate the menu. " + " selects submenus --->. " + "Highlighted letters are hotkeys. " + "Pressing selectes a feature, while will exclude a feature. " + "Press to exit, for Help, for Search. " + "Legend: [*] feature is selected [ ] feature is excluded", +radiolist_instructions[] = + "Use the arrow keys to navigate this window or " + "press the hotkey of the item you wish to select " + "followed by the . " + "Press for additional information about this option.", +inputbox_instructions_int[] = + "Please enter a decimal value. " + "Fractions will not be accepted. " + "Use the key to move from the input field to the buttons below it.", +inputbox_instructions_hex[] = + "Please enter a hexadecimal value. " + "Use the key to move from the input field to the buttons below it.", +inputbox_instructions_string[] = + "Please enter a string value. " + "Use the key to move from the input field to the buttons below it.", +setmod_text[] = + "This feature depends on another which has been configured as a module.\n" + "As a result, this feature will be built as a module.", +nohelp_text[] = + "There is no help available for this option.\n", +load_config_text[] = + "Enter the name of the configuration file you wish to load. " + "Accept the name shown to restore the configuration you " + "last retrieved. Leave blank to abort.", +load_config_help[] = + "\n" + "For various reasons, one may wish to keep several different axTLS\n" + "configurations available on a single machine.\n" + "\n" + "If you have saved a previous configuration in a file other than the\n" + "axTLS's default, entering the name of the file here will allow you\n" + "to modify that configuration.\n" + "\n" + "If you are uncertain, then you have probably never used alternate\n" + "configuration files. You should therefor leave this blank to abort.\n", +save_config_text[] = + "Enter a filename to which this configuration should be saved " + "as an alternate. Leave blank to abort.", +save_config_help[] = + "\n" + "For various reasons, one may wish to keep different axTLS\n" + "configurations available on a single machine.\n" + "\n" + "Entering a file name here will allow you to later retrieve, modify\n" + "and use the current configuration as an alternate to whatever\n" + "configuration options you have selected at that time.\n" + "\n" + "If you are uncertain what all this means then you should probably\n" + "leave this blank.\n", +search_help[] = + "\n" + "Search for CONFIG_ symbols and display their relations.\n" + "Example: search for \"^FOO\"\n" + "Result:\n" + "-----------------------------------------------------------------\n" + "Symbol: FOO [=m]\n" + "Prompt: Foo bus is used to drive the bar HW\n" + "Defined at drivers/pci/Kconfig:47\n" + "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" + "Location:\n" + " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n" + " -> PCI support (PCI [=y])\n" + " -> PCI access mode ( [=y])\n" + "Selects: LIBCRC32\n" + "Selected by: BAR\n" + "-----------------------------------------------------------------\n" + "o The line 'Prompt:' shows the text used in the menu structure for\n" + " this CONFIG_ symbol\n" + "o The 'Defined at' line tell at what file / line number the symbol\n" + " is defined\n" + "o The 'Depends on:' line tell what symbols needs to be defined for\n" + " this symbol to be visible in the menu (selectable)\n" + "o The 'Location:' lines tell where in the menu structure this symbol\n" + " is located\n" + " A location followed by a [=y] indicate that this is a selectable\n" + " menu item - and current value is displayed inside brackets.\n" + "o The 'Selects:' line tell what symbol will be automatically\n" + " selected if this symbol is selected (y or m)\n" + "o The 'Selected by' line tell what symbol has selected this symbol\n" + "\n" + "Only relevant lines are shown.\n" + "\n\n" + "Search examples:\n" + "Examples: USB => find all CONFIG_ symbols containing USB\n" + " ^USB => find all CONFIG_ symbols starting with USB\n" + " USB$ => find all CONFIG_ symbols ending with USB\n" + "\n"; + +static char filename[PATH_MAX+1] = ".config"; +static int indent; +static struct termios ios_org; +static int rows = 0, cols = 0; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; + +static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */ +static int item_no; + +static void conf(struct menu *menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static void show_textbox(const char *title, const char *text, int r, int c); +static void show_helptext(const char *title, const char *text); +static void show_help(struct menu *menu); +static void show_file(const char *filename, const char *title, int r, int c); + +static void init_wsize(void) +{ + struct winsize ws; + char *env; + + if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) { + rows = ws.ws_row; + cols = ws.ws_col; + } + + if (!rows) { + env = getenv("LINES"); + if (env) + rows = atoi(env); + if (!rows) + rows = 24; + } + if (!cols) { + env = getenv("COLUMNS"); + if (env) + cols = atoi(env); + if (!cols) + cols = 80; + } + + if (rows < 19 || cols < 80) { + fprintf(stderr, "Your display is too small to run Menuconfig!\n"); + fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); + exit(1); + } + + rows -= 4; + cols -= 5; +} + +static void cinit(void) +{ + item_no = 0; +} + +static void cmake(void) +{ + items[item_no] = malloc(sizeof(struct dialog_list_item)); + memset(items[item_no], 0, sizeof(struct dialog_list_item)); + items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0; + items[item_no]->name = malloc(512); items[item_no]->name[0] = 0; + items[item_no]->namelen = 0; + item_no++; +} + +static int cprint_name(const char *fmt, ...) +{ + va_list ap; + int res; + + if (!item_no) + cmake(); + va_start(ap, fmt); + res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen, + 512 - items[item_no - 1]->namelen, fmt, ap); + if (res > 0) + items[item_no - 1]->namelen += res; + va_end(ap); + + return res; +} + +static int cprint_tag(const char *fmt, ...) +{ + va_list ap; + int res; + + if (!item_no) + cmake(); + va_start(ap, fmt); + res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap); + va_end(ap); + + return res; +} + +static void cdone(void) +{ + int i; + + for (i = 0; i < item_no; i++) { + free(items[i]->tag); + free(items[i]->name); + free(items[i]); + } + + item_no = 0; +} + +static void get_prompt_str(struct gstr *r, struct property *prop) +{ + int i, j; + struct menu *submenu[8], *menu; + + str_printf(r, "Prompt: %s\n", prop->text); + str_printf(r, " Defined at %s:%d\n", prop->menu->file->name, + prop->menu->lineno); + if (!expr_is_yes(prop->visible.expr)) { + str_append(r, " Depends on: "); + expr_gstr_print(prop->visible.expr, r); + str_append(r, "\n"); + } + menu = prop->menu->parent; + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) + submenu[i++] = menu; + if (i > 0) { + str_printf(r, " Location:\n"); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu)); + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : "", + sym_get_string_value(menu->sym)); + } + str_append(r, "\n"); + } + } +} + +static void get_symbol_str(struct gstr *r, struct symbol *sym) +{ + bool hit; + struct property *prop; + + str_printf(r, "Symbol: %s [=%s]\n", sym->name, + sym_get_string_value(sym)); + for_all_prompts(sym, prop) + get_prompt_str(r, prop); + hit = false; + for_all_properties(sym, prop, P_SELECT) { + if (!hit) { + str_append(r, " Selects: "); + hit = true; + } else + str_printf(r, " && "); + expr_gstr_print(prop->expr, r); + } + if (hit) + str_append(r, "\n"); + if (sym->rev_dep.expr) { + str_append(r, " Selected by: "); + expr_gstr_print(sym->rev_dep.expr, r); + str_append(r, "\n"); + } + str_append(r, "\n\n"); +} + +static struct gstr get_relations_str(struct symbol **sym_arr) +{ + struct symbol *sym; + struct gstr res = str_new(); + int i; + + for (i = 0; sym_arr && (sym = sym_arr[i]); i++) + get_symbol_str(&res, sym); + if (!i) + str_append(&res, "No matches found.\n"); + return res; +} + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + +again: + switch (dialog_inputbox("Search Configuration Parameter", + "Enter Keyword", 10, 75, + NULL)) { + case 0: + break; + case 1: + show_helptext("Search Configuration", search_help); + goto again; + default: + return; + } + + sym_arr = sym_re_search(dialog_input_result); + res = get_relations_str(sym_arr); + free(sym_arr); + show_textbox("Search Results", str_get(&res), 0, 0); + str_free(&res); +} + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + switch (prop->type) { + case P_MENU: + child_count++; + cmake(); + cprint_tag("m%p", menu); + + if (single_menu_mode) { + cprint_name("%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else { + cprint_name(" %*c%s --->", indent + 1, ' ', prompt); + } + + if (single_menu_mode && menu->data) + goto conf_childs; + return; + default: + if (prompt) { + child_count++; + cmake(); + cprint_tag(":%p", menu); + cprint_name("---%*c%s", indent + 1, ' ', prompt); + } + } + } else + doint = 0; + goto conf_childs; + } + + cmake(); + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + cprint_tag("t%p", menu); + switch (type) { + case S_BOOLEAN: + cprint_name("[%c]", val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + cprint_name("<%c>", ch); + break; + } + } else { + cprint_tag("%c%p", def_menu ? 't' : ':', menu); + cprint_name(" "); + } + + cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); + if (val == yes) { + if (def_menu) { + cprint_name(" (%s)", menu_get_prompt(def_menu)); + cprint_name(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + cprint_tag(":%p", menu); + cprint_name("---%*c%s", indent + 1, ' ', menu_get_prompt(menu)); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + cprint_tag(":%p", menu); + cprint_name(" "); + } else { + switch (type) { + case S_BOOLEAN: + cprint_tag("t%p", menu); + if (sym_is_changable(sym)) + cprint_name("[%c]", val == no ? ' ' : '*'); + else + cprint_name("---"); + break; + case S_TRISTATE: + cprint_tag("t%p", menu); + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + if (sym_is_changable(sym)) + cprint_name("<%c>", ch); + else + cprint_name("---"); + break; + default: + cprint_tag("s%p", menu); + tmp = cprint_name("(%s)", sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : " (NEW)"); + goto conf_childs; + } + } + cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : " (NEW)"); + if (menu->prompt->type == P_MENU) { + cprint_name(" --->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void conf(struct menu *menu) +{ + struct dialog_list_item *active_item = NULL; + struct menu *submenu; + const char *prompt = menu_get_prompt(menu); + struct symbol *sym; + char active_entry[40]; + int stat, type; + + unlink("lxdialog.scrltmp"); + active_entry[0] = 0; + while (1) { + indent = 0; + child_count = 0; + current_menu = menu; + cdone(); cinit(); + build_conf(menu); + if (!child_count) + break; + if (menu == &rootmenu) { + cmake(); cprint_tag(":"); cprint_name("--- "); + cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File"); + cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File"); + } + dialog_clear(); + stat = dialog_menu(prompt ? prompt : "Main Menu", + menu_instructions, rows, cols, rows - 10, + active_entry, item_no, items); + if (stat < 0) + return; + + if (stat == 1 || stat == 255) + break; + + active_item = first_sel_item(item_no, items); + if (!active_item) + continue; + active_item->selected = 0; + strncpy(active_entry, active_item->tag, sizeof(active_entry)); + active_entry[sizeof(active_entry)-1] = 0; + type = active_entry[0]; + if (!type) + continue; + + sym = NULL; + submenu = NULL; + if (sscanf(active_entry + 1, "%p", &submenu) == 1) + sym = submenu->sym; + + switch (stat) { + case 0: + switch (type) { + case 'm': + if (single_menu_mode) + submenu->data = (void *) (long) !submenu->data; + else + conf(submenu); + break; + case 't': + if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt->type == P_MENU) + conf(submenu); + break; + case 's': + conf_string(submenu); + break; + case 'L': + conf_load(); + break; + case 'S': + conf_save(); + break; + } + break; + case 2: + if (sym) + show_help(submenu); + else + show_helptext("README", mconf_readme); + break; + case 3: + if (type == 't') { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + show_textbox(NULL, setmod_text, 6, 74); + } + break; + case 4: + if (type == 't') + sym_set_tristate_value(sym, no); + break; + case 5: + if (type == 't') + sym_set_tristate_value(sym, mod); + break; + case 6: + if (type == 't') + sym_toggle_tristate_value(sym); + else if (type == 'm') + conf(submenu); + break; + case 7: + search_conf(); + break; + } + } +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + int fd; + + fd = creat(".help.tmp", 0777); + write(fd, text, strlen(text)); + close(fd); + show_file(".help.tmp", title, r, c); + unlink(".help.tmp"); +} + +static void show_helptext(const char *title, const char *text) +{ + show_textbox(title, text, 0, 0); +} + +static void show_help(struct menu *menu) +{ + struct gstr help = str_new(); + struct symbol *sym = menu->sym; + + if (sym->help) + { + if (sym->name) { + str_printf(&help, "%s:\n\n", sym->name); + str_append(&help, sym->help); + str_append(&help, "\n"); + } + } else { + str_append(&help, nohelp_text); + } + get_symbol_str(&help, sym); + show_helptext(menu_get_prompt(menu), str_get(&help)); + str_free(&help); +} + +static void show_file(const char *filename, const char *title, int r, int c) +{ + while (dialog_textbox(title, filename, r ? r : rows, c ? c : cols) < 0) + ; +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + struct menu *child; + struct symbol *active; + + active = sym_get_choice_value(menu->sym); + while (1) { + current_menu = menu; + cdone(); cinit(); + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + cmake(); + cprint_tag("%p", child); + cprint_name("%s", menu_get_prompt(child)); + if (child->sym == sym_get_choice_value(menu->sym)) + items[item_no - 1]->selected = 1; /* ON */ + else if (child->sym == active) + items[item_no - 1]->selected = 2; /* SELECTED */ + else + items[item_no - 1]->selected = 0; /* OFF */ + } + + switch (dialog_checklist(prompt ? prompt : "Main Menu", + radiolist_instructions, 15, 70, 6, + item_no, items, FLAG_RADIO)) { + case 0: + if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1) + break; + sym_set_tristate_value(child->sym, yes); + return; + case 1: + if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) { + show_help(child); + active = child->sym; + } else + show_help(menu); + break; + case 255: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = (char *) inputbox_instructions_int; + break; + case S_HEX: + heading = (char *) inputbox_instructions_hex; + break; + case S_STRING: + heading = (char *) inputbox_instructions_string; + break; + default: + heading = "Internal mconf error!"; + /* panic? */; + } + + switch (dialog_inputbox(prompt ? prompt : "Main Menu", + heading, 10, 75, + sym_get_string_value(menu->sym))) { + case 0: + if (sym_set_string_value(menu->sym, dialog_input_result)) + return; + show_textbox(NULL, "You have made an invalid entry.", 5, 43); + break; + case 1: + show_help(menu); + break; + case 255: + return; + } + } +} + +static void conf_load(void) +{ + while (1) { + switch (dialog_inputbox(NULL, load_config_text, 11, 55, + filename)) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) + return; + show_textbox(NULL, "File does not exist!", 5, 38); + break; + case 1: + show_helptext("Load Alternate Configuration", load_config_help); + break; + case 255: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + switch (dialog_inputbox(NULL, save_config_text, 11, 55, + filename)) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_write(dialog_input_result)) + return; + show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60); + break; + case 1: + show_helptext("Save Alternate Configuration", save_config_help); + break; + case 255: + return; + } + } +} + +static void conf_cleanup(void) +{ + tcsetattr(1, TCSAFLUSH, &ios_org); + unlink(".help.tmp"); +} + +static void winch_handler(int sig) +{ + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1) { + rows = 24; + cols = 80; + } else { + rows = ws.ws_row; + cols = ws.ws_col; + } + + if (rows < 19 || cols < 80) { + end_dialog(); + fprintf(stderr, "Your display is too small to run Menuconfig!\n"); + fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); + exit(1); + } + + rows -= 4; + cols -= 5; + +} + +int main(int ac, char **av) +{ + struct symbol *sym; + char *mode; + int stat; + + conf_parse(av[1]); + conf_read(NULL); + + sym = sym_lookup("VERSION", 0); + sym_calc_value(sym); + snprintf(menu_backtitle, 128, "axTLS v%s Configuration", + sym_get_string_value(sym)); + + mode = getenv("MENUCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + tcgetattr(1, &ios_org); + atexit(conf_cleanup); + init_wsize(); + init_dialog(); + signal(SIGWINCH, winch_handler); + conf(&rootmenu); + end_dialog(); + + /* Restart dialog to act more like when lxdialog was still separate */ + init_dialog(); + do { + stat = dialog_yesno(NULL, + "Do you wish to save your new axTLS configuration?", 5, 60); + } while (stat < 0); + end_dialog(); + + if (stat == 0) { + conf_write(NULL); + printf("\n\n" + "*** End of axTLS configuration.\n" + "*** Check the top-level Makefile for additional configuration options.\n\n"); + } else + printf("\n\nYour axTLS configuration changes were NOT saved.\n\n"); + + return 0; +} diff --git a/libs/nixio/axTLS/config/scripts/config/menu.c b/libs/nixio/axTLS/config/scripts/config/menu.c new file mode 100644 index 000000000..0c13156f3 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/menu.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct menu rootmenu; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +static void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = malloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; +} + +void menu_end_entry(void) +{ +} + +void menu_add_menu(void) +{ + current_menu = current_entry; + last_entry_ptr = ¤t_entry->list; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n", + sym->name ? sym->name : "", + sym_type_name(sym->type), sym_type_name(type)); +} + +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->text = prompt; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (current_entry->prompt) + menu_warn(current_entry, "prompt redefined\n"); + current_entry->prompt = prop; + } + + return prop; +} + +void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%'" + " must be a single symbol", sym->name); + break; + case P_SELECT: + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses select, but is " + "not boolean or tristate", sym->name); + else if (sym2->type == S_UNKNOWN) + prop_warn(prop, + "'select' used by config symbol '%s' " + "refer to undefined symbol '%s'", + sym->name, sym2->name); + else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. 'select' only " + "accept arguments of boolean and " + "tristate type", sym2->name); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!sym_string_valid(sym, prop->expr->left.sym->name) || + !sym_string_valid(sym, prop->expr->right.sym->name)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + /* find the first choice value and find out choice type */ + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym) { + current_entry = parent; + menu_set_type(menu->sym->type); + current_entry = menu; + menu_set_type(sym->type); + break; + } + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && menu->sym) { + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_PROMPT && prop->menu != menu) { + prop_warn(prop, "choice values " + "currently only support a " + "single prompt"); + } + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + } + current_entry = menu; + menu_set_type(sym->type); + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_CHOICE, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined " + "without type\n"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt\n"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) + if (menu_is_visible(child)) + return true; + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + diff --git a/libs/nixio/axTLS/config/scripts/config/mkconfigs b/libs/nixio/axTLS/config/scripts/config/mkconfigs new file mode 100755 index 000000000..3cb7bb175 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/mkconfigs @@ -0,0 +1,67 @@ +#!/bin/sh +# +# Copyright (C) 2002 Khalid Aziz +# Copyright (C) 2002 Randy Dunlap +# Copyright (C) 2002 Al Stone +# Copyright (C) 2002 Hewlett-Packard Company +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Busybox version by Matteo Croce <3297627799 at wind.it> +# +# Rules to generate bbconfig.h from .config: +# - Retain lines that begin with "CONFIG_" +# - Retain lines that begin with "# CONFIG_" +# - lines that use double-quotes must \\-escape-quote them + +if [ $# -lt 1 ] +then + config=.config +else config=$1 +fi + +echo "#ifndef _BBCONFIG_H" +echo "#define _BBCONFIG_H" +echo \ +"/* + * busybox configuration options. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * This file is generated automatically by scripts/config/mkconfigs. + * Do not edit. + * + */" + +echo "static char const bbconfig_config[] =" +echo "\"CONFIG_BEGIN=n\\n\\" +echo "`sed 's/\"/\\\\\"/g' $config | grep "^#\? \?CONFIG_" | awk '{ print $0 "\\\\n\\\\" }' `" +echo "CONFIG_END=n\\n\";" +echo "#endif /* _BBCONFIG_H */" diff --git a/libs/nixio/axTLS/config/scripts/config/symbol.c b/libs/nixio/axTLS/config/scripts/config/symbol.c new file mode 100644 index 000000000..ea629728a --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/symbol.c @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_YES|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_MOD|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_NO|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +int sym_change_count; +struct symbol *modules_sym; +tristate modules_val; + +void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, 1)); +} + +void sym_init(void) +{ + struct symbol *sym; + char *p; + static bool inited = false; + + if (inited) + return; + inited = true; + + sym = sym_lookup("VERSION", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + p = getenv("VERSION"); + if (p) + sym_add_default(sym, p); + + sym = sym_lookup("TARGET_ARCH", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + p = getenv("TARGET_ARCH"); + if (p) + sym_add_default(sym, p); + +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + tristate tri; + + /* any prompt visible? */ + tri = no; + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + tri = E_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* is the user choice visible? */ + def_sym = sym->user.val; + if (def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) { + def_sym = e->right.sym; + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + return def_sym; + } + + /* no choice? reset tristate value */ + sym->curr.tri = no; + return NULL; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) + newval.tri = sym->user.tri; + else if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) + newval.tri = expr_calc_value(prop->expr); + } + newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri); + } else if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = expr_calc_value(prop->expr); + } + } + if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->user.val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) + sym_set_changed(sym); + if (modules_sym == sym) + modules_val = modules_sym->curr.tri; + + if (sym_is_choice(sym)) { + int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); + prop = sym_get_choice_prop(sym); + for (e = prop->expr; e; e = e->left.expr) { + e->right.sym->flags |= flags; + if (flags & SYMBOL_CHANGED) + sym_set_changed(e->right.sym); + } + } +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_change_count++; + if (modules_sym) + sym_calc_value(modules_sym); +} + +void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (sym->flags & SYMBOL_NEW) { + sym->flags &= ~SYMBOL_NEW; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + + cs->user.val = sym; + cs->flags &= ~SYMBOL_NEW; + } + + sym->user.tri = val; + if (oldval != val) { + sym_clear_all_valid(); + if (sym == modules_sym) + sym_set_all_changed(); + } + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + signed char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + int val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 10); + return val >= strtol(prop->expr->left.sym->name, NULL, 10) && + val <= strtol(prop->expr->right.sym->name, NULL, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtol(str, NULL, 16); + return val >= strtol(prop->expr->left.sym->name, NULL, 16) && + val <= strtol(prop->expr->right.sym->name, NULL, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (sym->flags & SYMBOL_NEW) { + sym->flags &= ~SYMBOL_NEW; + sym_set_changed(sym); + } + + oldval = sym->user.val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->user.val = val = malloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->user.val = val = malloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + return "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +struct symbol *sym_lookup(const char *name, int isconst) +{ + struct symbol *symbol; + const char *ptr; + char *new_name; + int hash = 0; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name)) { + if ((isconst && symbol->flags & SYMBOL_CONST) || + (!isconst && !(symbol->flags & SYMBOL_CONST))) + return symbol; + } + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 256; + } + + symbol = malloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + symbol->flags = SYMBOL_NEW; + if (isconst) + symbol->flags |= SYMBOL_CONST; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + const char *ptr; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + for (ptr = name; *ptr; ptr++) + hash += *ptr; + hash &= 0xff; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (!strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +struct symbol **sym_re_search(const char *pattern) +{ + struct symbol *sym, **sym_arr = NULL; + int i, cnt, size; + regex_t re; + + cnt = size = 0; + /* Skip if empty */ + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE)) + return NULL; + + for_all_symbols(i, sym) { + if (sym->flags & SYMBOL_CONST || !sym->name) + continue; + if (regexec(&re, sym->name, 0, NULL, 0)) + continue; + if (cnt + 1 >= size) { + void *tmp = sym_arr; + size += 16; + sym_arr = realloc(sym_arr, size * sizeof(struct symbol *)); + if (!sym_arr) { + free(tmp); + return NULL; + } + } + sym_arr[cnt++] = sym; + } + if (sym_arr) + sym_arr[cnt] = NULL; + regfree(&re); + + return sym_arr; +} + + +struct symbol *sym_check_deps(struct symbol *sym); + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK_DONE) + return NULL; + if (sym->flags & SYMBOL_CHECK) { + printf("Warning! Found recursive dependency: %s", sym->name); + return sym; + } + + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + goto out; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + goto out; + } +out: + if (sym2) + printf(" %s", sym->name); + sym->flags &= ~SYMBOL_CHECK; + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = malloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_CHOICE)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_RANGE: + return "range"; + case P_UNKNOWN: + break; + } + return "unknown"; +} diff --git a/libs/nixio/axTLS/config/scripts/config/util.c b/libs/nixio/axTLS/config/scripts/config/util.c new file mode 100644 index 000000000..8fc95a2a7 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/util.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2002-2005 Roman Zippel + * Copyright (C) 2002-2005 Sam Ravnborg + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include "lkc.h" + +/* file already present in list? If not add it */ +struct file *file_lookup(const char *name) +{ + struct file *file; + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) + return file; + } + + file = malloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = strdup(name); + file->next = file_list; + file_list = file; + return file; +} + +/* write a dependency file as used by kbuild to track dependencies */ +int file_write_dep(const char *name) +{ + struct file *file; + FILE *out; + + if (!name) + name = "config/.config.cmd"; + out = fopen("config/.config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n"); + fclose(out); + rename(".config.tmp", name); + return 0; +} + + +/* Allocate initial growable sting */ +struct gstr str_new(void) +{ + struct gstr gs; + gs.s = malloc(sizeof(char) * 64); + gs.len = 16; + strcpy(gs.s, "\0"); + return gs; +} + +/* Allocate and assign growable string */ +struct gstr str_assign(const char *s) +{ + struct gstr gs; + gs.s = strdup(s); + gs.len = strlen(s) + 1; + return gs; +} + +/* Free storage for growable string */ +void str_free(struct gstr *gs) +{ + if (gs->s) + free(gs->s); + gs->s = NULL; + gs->len = 0; +} + +/* Append to growable string */ +void str_append(struct gstr *gs, const char *s) +{ + size_t l = strlen(gs->s) + strlen(s) + 1; + if (l > gs->len) { + gs->s = realloc(gs->s, l); + gs->len = l; + } + strcat(gs->s, s); +} + +/* Append printf formatted string to growable string */ +void str_printf(struct gstr *gs, const char *fmt, ...) +{ + va_list ap; + char s[10000]; /* big enough... */ + va_start(ap, fmt); + vsnprintf(s, sizeof(s), fmt, ap); + str_append(gs, s); + va_end(ap); +} + +/* Retreive value of growable string */ +const char *str_get(struct gstr *gs) +{ + return gs->s; +} + diff --git a/libs/nixio/axTLS/config/scripts/config/zconf.l b/libs/nixio/axTLS/config/scripts/config/zconf.l new file mode 100644 index 000000000..55517b287 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/zconf.l @@ -0,0 +1,366 @@ +%option backup nostdinit noyywrap never-interactive full ecs +%option 8bit backup nodefault perf-report perf-report +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +char *text; +static char *text_ptr; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static struct buffer *zconf_endfile(void); + +void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_ptr = text; + text_size = 0; + *text_ptr = 0; +} + +void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + text = realloc(text, new_size); + text_asize = new_size; + text_ptr = text + text_size; + } + memcpy(text_ptr, str, size); + text_ptr += size; + text_size += size; + *text_ptr = 0; +} + +void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} +%} + +ws [ \n\t] +n [A-Za-z0-9_] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n current_file->lineno++; +[ \t]*#.* + +[ \t]*\n current_file->lineno++; return T_EOL; + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +{ + "mainmenu" BEGIN(PARAM); return T_MAINMENU; + "menu" BEGIN(PARAM); return T_MENU; + "endmenu" BEGIN(PARAM); return T_ENDMENU; + "source" BEGIN(PARAM); return T_SOURCE; + "choice" BEGIN(PARAM); return T_CHOICE; + "endchoice" BEGIN(PARAM); return T_ENDCHOICE; + "comment" BEGIN(PARAM); return T_COMMENT; + "config" BEGIN(PARAM); return T_CONFIG; + "menuconfig" BEGIN(PARAM); return T_MENUCONFIG; + "help" BEGIN(PARAM); return T_HELP; + "if" BEGIN(PARAM); return T_IF; + "endif" BEGIN(PARAM); return T_ENDIF; + "depends" BEGIN(PARAM); return T_DEPENDS; + "requires" BEGIN(PARAM); return T_REQUIRES; + "optional" BEGIN(PARAM); return T_OPTIONAL; + "default" BEGIN(PARAM); return T_DEFAULT; + "prompt" BEGIN(PARAM); return T_PROMPT; + "tristate" BEGIN(PARAM); return T_TRISTATE; + "def_tristate" BEGIN(PARAM); return T_DEF_TRISTATE; + "bool" BEGIN(PARAM); return T_BOOLEAN; + "boolean" BEGIN(PARAM); return T_BOOLEAN; + "def_bool" BEGIN(PARAM); return T_DEF_BOOLEAN; + "def_boolean" BEGIN(PARAM); return T_DEF_BOOLEAN; + "int" BEGIN(PARAM); return T_INT; + "hex" BEGIN(PARAM); return T_HEX; + "string" BEGIN(PARAM); return T_STRING; + "select" BEGIN(PARAM); return T_SELECT; + "enable" BEGIN(PARAM); return T_SELECT; + "range" BEGIN(PARAM); return T_RANGE; + {n}+ { + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . + \n current_file->lineno++; BEGIN(INITIAL); +} + +{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + "if" return T_IF; + "on" return T_ON; + \"|\' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + --- /* ignore */ + ({n}|[-/.])+ { + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\\n current_file->lineno++; + . + <> { + BEGIN(INITIAL); + } +} + +{ + [^'"\\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\\n]+ { + append_string(yytext, yyleng); + } + \\.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \\.? { + append_string(yytext + 1, yyleng - 1); + } + \'|\" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <> { + BEGIN(INITIAL); + } +} + +{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<> { + if (current_buf) { + zconf_endfile(); + return T_EOF; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(name); + if (!yyin) { + printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("recursive scan (%s)?\n", name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("file %s already scanned?\n", name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static struct buffer *zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; + + return parent; +} + +int zconf_lineno(void) +{ + if (current_buf) + return current_file->lineno - 1; + else + return 0; +} + +char *zconf_curname(void) +{ + if (current_buf) + return current_file->name; + else + return ""; +} diff --git a/libs/nixio/axTLS/config/scripts/config/zconf.tab.c_shipped b/libs/nixio/axTLS/config/scripts/config/zconf.tab.c_shipped new file mode 100644 index 000000000..cc68dcb9a --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/zconf.tab.c_shipped @@ -0,0 +1,2130 @@ +/* A Bison parser, made by GNU Bison 1.875a. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* If NAME_PREFIX is specified substitute the variables and functions + names. */ +#define yyparse zconfparse +#define yylex zconflex +#define yyerror zconferror +#define yylval zconflval +#define yychar zconfchar +#define yydebug zconfdebug +#define yynerrs zconfnerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_MENUCONFIG = 266, + T_HELP = 267, + T_HELPTEXT = 268, + T_IF = 269, + T_ENDIF = 270, + T_DEPENDS = 271, + T_REQUIRES = 272, + T_OPTIONAL = 273, + T_PROMPT = 274, + T_DEFAULT = 275, + T_TRISTATE = 276, + T_DEF_TRISTATE = 277, + T_BOOLEAN = 278, + T_DEF_BOOLEAN = 279, + T_STRING = 280, + T_INT = 281, + T_HEX = 282, + T_WORD = 283, + T_WORD_QUOTE = 284, + T_UNEQUAL = 285, + T_EOF = 286, + T_EOL = 287, + T_CLOSE_PAREN = 288, + T_OPEN_PAREN = 289, + T_ON = 290, + T_SELECT = 291, + T_RANGE = 292, + T_OR = 293, + T_AND = 294, + T_EQUAL = 295, + T_NOT = 296 + }; +#endif +#define T_MAINMENU 258 +#define T_MENU 259 +#define T_ENDMENU 260 +#define T_SOURCE 261 +#define T_CHOICE 262 +#define T_ENDCHOICE 263 +#define T_COMMENT 264 +#define T_CONFIG 265 +#define T_MENUCONFIG 266 +#define T_HELP 267 +#define T_HELPTEXT 268 +#define T_IF 269 +#define T_ENDIF 270 +#define T_DEPENDS 271 +#define T_REQUIRES 272 +#define T_OPTIONAL 273 +#define T_PROMPT 274 +#define T_DEFAULT 275 +#define T_TRISTATE 276 +#define T_DEF_TRISTATE 277 +#define T_BOOLEAN 278 +#define T_DEF_BOOLEAN 279 +#define T_STRING 280 +#define T_INT 281 +#define T_HEX 282 +#define T_WORD 283 +#define T_WORD_QUOTE 284 +#define T_UNEQUAL 285 +#define T_EOF 286 +#define T_EOL 287 +#define T_CLOSE_PAREN 288 +#define T_OPEN_PAREN 289 +#define T_ON 290 +#define T_SELECT 291 +#define T_RANGE 292 +#define T_OR 293 +#define T_AND 294 +#define T_EQUAL 295 +#define T_NOT 296 + + + + +/* Copy the first part of user declarations. */ + + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(int token, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +static struct menu *current_menu, *current_entry; + +#define YYERROR_VERBOSE + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) + +typedef union YYSTYPE { + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} YYSTYPE; +/* Line 191 of yacc.c. */ + +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +#define LKC_DIRECT_LINK +#include "lkc.h" + + +/* Line 214 of yacc.c. */ + + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || (defined (_ALLOCA_H) && defined (__GNUC__)) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 201 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 42 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 41 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 104 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 182 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 296 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 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, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned short yyprhs[] = +{ + 0, 0, 3, 4, 7, 9, 11, 13, 17, 19, + 21, 23, 26, 28, 30, 32, 34, 36, 38, 42, + 45, 49, 52, 53, 56, 59, 62, 65, 69, 74, + 78, 83, 87, 91, 95, 100, 105, 110, 116, 119, + 122, 124, 128, 131, 132, 135, 138, 141, 144, 149, + 153, 157, 160, 165, 166, 169, 173, 175, 179, 182, + 183, 186, 189, 192, 196, 199, 201, 205, 208, 209, + 212, 215, 218, 222, 226, 228, 232, 235, 238, 241, + 242, 245, 248, 253, 257, 261, 262, 265, 267, 269, + 272, 275, 278, 280, 282, 283, 286, 288, 292, 296, + 300, 303, 307, 311, 313 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 43, 0, -1, -1, 43, 44, -1, 45, -1, 55, + -1, 66, -1, 3, 77, 79, -1, 5, -1, 15, + -1, 8, -1, 1, 79, -1, 61, -1, 71, -1, + 47, -1, 49, -1, 69, -1, 79, -1, 10, 28, + 32, -1, 46, 50, -1, 11, 28, 32, -1, 48, + 50, -1, -1, 50, 51, -1, 50, 75, -1, 50, + 73, -1, 50, 32, -1, 21, 76, 32, -1, 22, + 81, 80, 32, -1, 23, 76, 32, -1, 24, 81, + 80, 32, -1, 26, 76, 32, -1, 27, 76, 32, + -1, 25, 76, 32, -1, 19, 77, 80, 32, -1, + 20, 81, 80, 32, -1, 36, 28, 80, 32, -1, + 37, 82, 82, 80, 32, -1, 7, 32, -1, 52, + 56, -1, 78, -1, 53, 58, 54, -1, 53, 58, + -1, -1, 56, 57, -1, 56, 75, -1, 56, 73, + -1, 56, 32, -1, 19, 77, 80, 32, -1, 21, + 76, 32, -1, 23, 76, 32, -1, 18, 32, -1, + 20, 28, 80, 32, -1, -1, 58, 45, -1, 14, + 81, 32, -1, 78, -1, 59, 62, 60, -1, 59, + 62, -1, -1, 62, 45, -1, 62, 66, -1, 62, + 55, -1, 4, 77, 32, -1, 63, 74, -1, 78, + -1, 64, 67, 65, -1, 64, 67, -1, -1, 67, + 45, -1, 67, 66, -1, 67, 55, -1, 67, 1, + 32, -1, 6, 77, 32, -1, 68, -1, 9, 77, + 32, -1, 70, 74, -1, 12, 32, -1, 72, 13, + -1, -1, 74, 75, -1, 74, 32, -1, 16, 35, + 81, 32, -1, 16, 81, 32, -1, 17, 81, 32, + -1, -1, 77, 80, -1, 28, -1, 29, -1, 5, + 79, -1, 8, 79, -1, 15, 79, -1, 32, -1, + 31, -1, -1, 14, 81, -1, 82, -1, 82, 40, + 82, -1, 82, 30, 82, -1, 34, 81, 33, -1, + 41, 81, -1, 81, 38, 81, -1, 81, 39, 81, + -1, 28, -1, 29, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 94, 94, 95, 98, 99, 100, 101, 102, 103, + 104, 105, 109, 110, 111, 112, 113, 114, 120, 128, + 134, 142, 152, 154, 155, 156, 157, 160, 166, 173, + 179, 186, 192, 198, 204, 210, 216, 222, 230, 239, + 245, 254, 255, 261, 263, 264, 265, 266, 269, 275, + 281, 287, 293, 299, 301, 306, 315, 324, 325, 331, + 333, 334, 335, 340, 347, 353, 362, 363, 369, 371, + 372, 373, 374, 377, 383, 390, 397, 404, 410, 417, + 418, 419, 422, 427, 432, 440, 442, 447, 448, 451, + 452, 453, 457, 457, 459, 460, 463, 464, 465, 466, + 467, 468, 469, 472, 473 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", + "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", + "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", + "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_DEFAULT", "T_TRISTATE", + "T_DEF_TRISTATE", "T_BOOLEAN", "T_DEF_BOOLEAN", "T_STRING", "T_INT", + "T_HEX", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", "T_EOF", "T_EOL", + "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_ON", "T_SELECT", "T_RANGE", "T_OR", + "T_AND", "T_EQUAL", "T_NOT", "$accept", "input", "block", + "common_block", "config_entry_start", "config_stmt", + "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", + "config_option", "choice", "choice_entry", "choice_end", "choice_stmt", + "choice_option_list", "choice_option", "choice_block", "if", "if_end", + "if_stmt", "if_block", "menu", "menu_entry", "menu_end", "menu_stmt", + "menu_block", "source", "source_stmt", "comment", "comment_stmt", + "help_start", "help", "depends_list", "depends", "prompt_stmt_opt", + "prompt", "end", "nl_or_eof", "if_expr", "expr", "symbol", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 42, 43, 43, 44, 44, 44, 44, 44, 44, + 44, 44, 45, 45, 45, 45, 45, 45, 46, 47, + 48, 49, 50, 50, 50, 50, 50, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, + 54, 55, 55, 56, 56, 56, 56, 56, 57, 57, + 57, 57, 57, 58, 58, 59, 60, 61, 61, 62, + 62, 62, 62, 63, 64, 65, 66, 66, 67, 67, + 67, 67, 67, 68, 69, 70, 71, 72, 73, 74, + 74, 74, 75, 75, 75, 76, 76, 77, 77, 78, + 78, 78, 79, 79, 80, 80, 81, 81, 81, 81, + 81, 81, 81, 82, 82 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 3, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, + 3, 2, 0, 2, 2, 2, 2, 3, 4, 3, + 4, 3, 3, 3, 4, 4, 4, 5, 2, 2, + 1, 3, 2, 0, 2, 2, 2, 2, 4, 3, + 3, 2, 4, 0, 2, 3, 1, 3, 2, 0, + 2, 2, 2, 3, 2, 1, 3, 2, 0, 2, + 2, 2, 3, 3, 1, 3, 2, 2, 2, 0, + 2, 2, 4, 3, 3, 0, 2, 1, 1, 2, + 2, 2, 1, 1, 0, 2, 1, 3, 3, 3, + 2, 3, 3, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 8, 0, 0, 10, + 0, 0, 0, 0, 9, 93, 92, 3, 4, 22, + 14, 22, 15, 43, 53, 5, 59, 12, 79, 68, + 6, 74, 16, 79, 13, 17, 11, 87, 88, 0, + 0, 0, 38, 0, 0, 0, 103, 104, 0, 0, + 0, 96, 19, 21, 39, 42, 58, 64, 0, 76, + 7, 63, 73, 75, 18, 20, 0, 100, 55, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 0, + 85, 0, 85, 85, 85, 26, 0, 0, 23, 0, + 25, 24, 0, 0, 0, 85, 85, 47, 44, 46, + 45, 0, 0, 0, 54, 41, 40, 60, 62, 57, + 61, 56, 81, 80, 0, 69, 71, 66, 70, 65, + 99, 101, 102, 98, 97, 77, 0, 0, 0, 94, + 94, 0, 94, 94, 0, 94, 0, 0, 0, 94, + 0, 78, 51, 94, 94, 0, 0, 89, 90, 91, + 72, 0, 83, 84, 0, 0, 0, 27, 86, 0, + 29, 0, 33, 31, 32, 0, 94, 0, 0, 49, + 50, 82, 95, 34, 35, 28, 30, 36, 0, 48, + 52, 37 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const short yydefgoto[] = +{ + -1, 1, 17, 18, 19, 20, 21, 22, 52, 88, + 23, 24, 105, 25, 54, 98, 55, 26, 109, 27, + 56, 28, 29, 117, 30, 58, 31, 32, 33, 34, + 89, 90, 57, 91, 131, 132, 106, 35, 155, 50, + 51 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -99 +static const short yypact[] = +{ + -99, 48, -99, 38, 46, 46, -99, 46, -29, -99, + 46, -17, -3, -11, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99, -99, 38, + 12, 15, -99, 18, 51, 62, -99, -99, -11, -11, + 4, -24, 138, 138, 160, 121, 110, -4, 81, -4, + -99, -99, -99, -99, -99, -99, -19, -99, -99, -11, + -11, 70, 70, 73, 32, -11, 46, -11, 46, -11, + 46, -11, 46, 46, 46, -99, 36, 70, -99, 95, + -99, -99, 96, 46, 106, 46, 46, -99, -99, -99, + -99, 38, 38, 38, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, 112, -99, -99, -99, -99, -99, + -99, 117, -99, -99, -99, -99, -11, 33, 65, 131, + 1, 119, 131, 1, 136, 1, 153, 154, 155, 131, + 70, -99, -99, 131, 131, 156, 157, -99, -99, -99, + -99, 101, -99, -99, -11, 158, 159, -99, -99, 161, + -99, 162, -99, -99, -99, 163, 131, 164, 165, -99, + -99, -99, 99, -99, -99, -99, -99, -99, 166, -99, + -99, -99 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const short yypgoto[] = +{ + -99, -99, -99, 111, -99, -99, -99, -99, 178, -99, + -99, -99, -99, 91, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, 115, -99, -99, -99, -99, -99, + -99, 146, 168, 89, 27, 0, 126, -1, -98, -48, + -63 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -68 +static const short yytable[] = +{ + 66, 67, 36, 42, 39, 40, 71, 41, 123, 124, + 43, 44, 74, 75, 120, 154, 72, 46, 47, 69, + 70, 121, 122, 48, 140, 45, 127, 128, 112, 130, + 49, 133, 156, 135, 158, 159, 68, 161, 60, 69, + 70, 165, 69, 70, 61, 167, 168, 62, 2, 3, + 63, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 46, 47, 13, 14, 139, 152, 48, 126, 178, 15, + 16, 69, 70, 49, 37, 38, 129, 166, 151, 15, + 16, -67, 114, 64, -67, 5, 101, 7, 8, 102, + 10, 11, 12, 143, 65, 13, 103, 153, 46, 47, + 147, 148, 149, 69, 70, 125, 172, 134, 141, 136, + 137, 138, 15, 16, 5, 101, 7, 8, 102, 10, + 11, 12, 145, 146, 13, 103, 101, 7, 142, 102, + 10, 11, 12, 171, 144, 13, 103, 69, 70, 69, + 70, 15, 16, 100, 150, 154, 113, 108, 113, 116, + 73, 157, 15, 16, 74, 75, 70, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 104, 107, 160, 115, + 85, 110, 73, 118, 86, 87, 74, 75, 92, 93, + 94, 95, 111, 96, 119, 162, 163, 164, 169, 170, + 173, 174, 97, 175, 176, 177, 179, 180, 181, 53, + 99, 59 +}; + +static const unsigned char yycheck[] = +{ + 48, 49, 3, 32, 4, 5, 30, 7, 71, 72, + 10, 28, 16, 17, 33, 14, 40, 28, 29, 38, + 39, 69, 70, 34, 87, 28, 74, 75, 32, 77, + 41, 79, 130, 81, 132, 133, 32, 135, 39, 38, + 39, 139, 38, 39, 32, 143, 144, 32, 0, 1, + 32, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 28, 29, 14, 15, 28, 32, 34, 35, 166, 31, + 32, 38, 39, 41, 28, 29, 76, 140, 126, 31, + 32, 0, 1, 32, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 93, 32, 14, 15, 32, 28, 29, + 101, 102, 103, 38, 39, 32, 154, 80, 13, 82, + 83, 84, 31, 32, 4, 5, 6, 7, 8, 9, + 10, 11, 95, 96, 14, 15, 5, 6, 32, 8, + 9, 10, 11, 32, 28, 14, 15, 38, 39, 38, + 39, 31, 32, 54, 32, 14, 57, 56, 59, 58, + 12, 32, 31, 32, 16, 17, 39, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 55, 56, 32, 58, + 32, 56, 12, 58, 36, 37, 16, 17, 18, 19, + 20, 21, 56, 23, 58, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 21, + 54, 33 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 43, 0, 1, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 14, 15, 31, 32, 44, 45, 46, + 47, 48, 49, 52, 53, 55, 59, 61, 63, 64, + 66, 68, 69, 70, 71, 79, 79, 28, 29, 77, + 77, 77, 32, 77, 28, 28, 28, 29, 34, 41, + 81, 82, 50, 50, 56, 58, 62, 74, 67, 74, + 79, 32, 32, 32, 32, 32, 81, 81, 32, 38, + 39, 30, 40, 12, 16, 17, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 32, 36, 37, 51, 72, + 73, 75, 18, 19, 20, 21, 23, 32, 57, 73, + 75, 5, 8, 15, 45, 54, 78, 45, 55, 60, + 66, 78, 32, 75, 1, 45, 55, 65, 66, 78, + 33, 81, 81, 82, 82, 32, 35, 81, 81, 77, + 81, 76, 77, 81, 76, 81, 76, 76, 76, 28, + 82, 13, 32, 77, 28, 76, 76, 79, 79, 79, + 32, 81, 32, 32, 14, 80, 80, 32, 80, 80, + 32, 80, 32, 32, 32, 80, 82, 80, 80, 32, + 32, 32, 81, 32, 32, 32, 32, 32, 80, 32, + 32, 32 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.first_line = Rhs[1].first_line; \ + Current.first_column = Rhs[1].first_column; \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (cinluded). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylineno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylineno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 8: + + { zconfprint("unexpected 'endmenu' statement"); ;} + break; + + case 9: + + { zconfprint("unexpected 'endif' statement"); ;} + break; + + case 10: + + { zconfprint("unexpected 'endchoice' statement"); ;} + break; + + case 11: + + { zconfprint("syntax error"); yyerrok; ;} + break; + + case 18: + + { + struct symbol *sym = sym_lookup(yyvsp[-1].string, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 19: + + { + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 20: + + { + struct symbol *sym = sym_lookup(yyvsp[-1].string, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 21: + + { + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 27: + + { + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 28: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 29: + + { + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 30: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 31: + + { + menu_set_type(S_INT); + printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 32: + + { + menu_set_type(S_HEX); + printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 33: + + { + menu_set_type(S_STRING); + printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 34: + + { + menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 35: + + { + menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 36: + + { + menu_add_symbol(P_SELECT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 37: + + { + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,yyvsp[-3].symbol, yyvsp[-2].symbol), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 38: + + { + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 39: + + { + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 40: + + { + if (zconf_endtoken(yyvsp[0].token, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 42: + + { + printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 48: + + { + menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 49: + + { + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 50: + + { + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 51: + + { + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 52: + + { + menu_add_symbol(P_DEFAULT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 55: + + { + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep(yyvsp[-1].expr); + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 56: + + { + if (zconf_endtoken(yyvsp[0].token, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 58: + + { + printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 63: + + { + menu_add_entry(NULL); + menu_add_prop(P_MENU, yyvsp[-1].string, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 64: + + { + menu_end_entry(); + menu_add_menu(); +;} + break; + + case 65: + + { + if (zconf_endtoken(yyvsp[0].token, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +;} + break; + + case 67: + + { + printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +;} + break; + + case 72: + + { zconfprint("invalid menu option"); yyerrok; ;} + break; + + case 73: + + { + yyval.string = yyvsp[-1].string; + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string); +;} + break; + + case 74: + + { + zconf_nextfile(yyvsp[0].string); +;} + break; + + case 75: + + { + menu_add_entry(NULL); + menu_add_prop(P_COMMENT, yyvsp[-1].string, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 76: + + { + menu_end_entry(); +;} + break; + + case 77: + + { + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +;} + break; + + case 78: + + { + current_entry->sym->help = yyvsp[0].string; +;} + break; + + case 82: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 83: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 84: + + { + menu_add_dep(yyvsp[-1].expr); + printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); +;} + break; + + case 86: + + { + menu_add_prop(P_PROMPT, yyvsp[-1].string, NULL, yyvsp[0].expr); +;} + break; + + case 89: + + { yyval.token = T_ENDMENU; ;} + break; + + case 90: + + { yyval.token = T_ENDCHOICE; ;} + break; + + case 91: + + { yyval.token = T_ENDIF; ;} + break; + + case 94: + + { yyval.expr = NULL; ;} + break; + + case 95: + + { yyval.expr = yyvsp[0].expr; ;} + break; + + case 96: + + { yyval.expr = expr_alloc_symbol(yyvsp[0].symbol); ;} + break; + + case 97: + + { yyval.expr = expr_alloc_comp(E_EQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;} + break; + + case 98: + + { yyval.expr = expr_alloc_comp(E_UNEQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;} + break; + + case 99: + + { yyval.expr = yyvsp[-1].expr; ;} + break; + + case 100: + + { yyval.expr = expr_alloc_one(E_NOT, yyvsp[0].expr); ;} + break; + + case 101: + + { yyval.expr = expr_alloc_two(E_OR, yyvsp[-2].expr, yyvsp[0].expr); ;} + break; + + case 102: + + { yyval.expr = expr_alloc_two(E_AND, yyvsp[-2].expr, yyvsp[0].expr); ;} + break; + + case 103: + + { yyval.symbol = sym_lookup(yyvsp[0].string, 0); free(yyvsp[0].string); ;} + break; + + case 104: + + { yyval.symbol = sym_lookup(yyvsp[0].string, 1); free(yyvsp[0].string); ;} + break; + + + } + +/* Line 999 of yacc.c. */ + + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("syntax error, unexpected ") + 1; + yysize += yystrlen (yytname[yytype]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* Return failure if at end of input. */ + if (yychar == YYEOF) + { + /* Pop the error token. */ + YYPOPSTACK; + /* Pop the rest of the stack. */ + while (yyss < yyssp) + { + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + YYPOPSTACK; + } + YYABORT; + } + + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*----------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action. | +`----------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + yyvsp--; + yystate = *--yyssp; + + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + + + + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup("MODULES", 0); + rootmenu.prompt = menu_add_prop(P_MENU, "axTLS Configuration", NULL, NULL); + + //zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym)) + printf("\n"); + else + sym->flags |= SYMBOL_CHECK_DONE; + } + + sym_change_count = 1; +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + } + return ""; +} + +static bool zconf_endtoken(int token, int starttoken, int endtoken) +{ + if (token != endtoken) { + zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfprint("location of the '%s'", zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (sym->help) { + int len = strlen(sym->help); + while (sym->help[--len] == '\n') + sym->help[len] = 0; + fprintf(out, " help\n%s\n", sym->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" + + diff --git a/libs/nixio/axTLS/config/scripts/config/zconf.tab.h_shipped b/libs/nixio/axTLS/config/scripts/config/zconf.tab.h_shipped new file mode 100644 index 000000000..3b191ef59 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/zconf.tab.h_shipped @@ -0,0 +1,125 @@ +/* A Bison parser, made from zconf.y, by GNU bison 1.75. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +#ifndef BISON_ZCONF_TAB_H +# define BISON_ZCONF_TAB_H + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_HELP = 266, + T_HELPTEXT = 267, + T_IF = 268, + T_ENDIF = 269, + T_DEPENDS = 270, + T_REQUIRES = 271, + T_OPTIONAL = 272, + T_PROMPT = 273, + T_DEFAULT = 274, + T_TRISTATE = 275, + T_BOOLEAN = 276, + T_INT = 277, + T_HEX = 278, + T_WORD = 279, + T_STRING = 280, + T_UNEQUAL = 281, + T_EOF = 282, + T_EOL = 283, + T_CLOSE_PAREN = 284, + T_OPEN_PAREN = 285, + T_ON = 286, + T_OR = 287, + T_AND = 288, + T_EQUAL = 289, + T_NOT = 290 + }; +#endif +#define T_MAINMENU 258 +#define T_MENU 259 +#define T_ENDMENU 260 +#define T_SOURCE 261 +#define T_CHOICE 262 +#define T_ENDCHOICE 263 +#define T_COMMENT 264 +#define T_CONFIG 265 +#define T_HELP 266 +#define T_HELPTEXT 267 +#define T_IF 268 +#define T_ENDIF 269 +#define T_DEPENDS 270 +#define T_REQUIRES 271 +#define T_OPTIONAL 272 +#define T_PROMPT 273 +#define T_DEFAULT 274 +#define T_TRISTATE 275 +#define T_BOOLEAN 276 +#define T_INT 277 +#define T_HEX 278 +#define T_WORD 279 +#define T_STRING 280 +#define T_UNEQUAL 281 +#define T_EOF 282 +#define T_EOL 283 +#define T_CLOSE_PAREN 284 +#define T_OPEN_PAREN 285 +#define T_ON 286 +#define T_OR 287 +#define T_AND 288 +#define T_EQUAL 289 +#define T_NOT 290 + + + + +#ifndef YYSTYPE +#line 33 "zconf.y" +typedef union { + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} yystype; +/* Line 1281 of /usr/share/bison/yacc.c. */ +#line 118 "zconf.tab.h" +# define YYSTYPE yystype +#endif + +extern YYSTYPE zconflval; + + +#endif /* not BISON_ZCONF_TAB_H */ + diff --git a/libs/nixio/axTLS/config/scripts/config/zconf.y b/libs/nixio/axTLS/config/scripts/config/zconf.y new file mode 100644 index 000000000..cf45da0b2 --- /dev/null +++ b/libs/nixio/axTLS/config/scripts/config/zconf.y @@ -0,0 +1,690 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(int token, int starttoken, int endtoken); + +struct symbol *symbol_hash[257]; + +static struct menu *current_menu, *current_entry; + +#define YYERROR_VERBOSE +%} +%expect 40 + +%union +{ + int token; + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; +} + +%token T_MAINMENU +%token T_MENU +%token T_ENDMENU +%token T_SOURCE +%token T_CHOICE +%token T_ENDCHOICE +%token T_COMMENT +%token T_CONFIG +%token T_MENUCONFIG +%token T_HELP +%token T_HELPTEXT +%token T_IF +%token T_ENDIF +%token T_DEPENDS +%token T_REQUIRES +%token T_OPTIONAL +%token T_PROMPT +%token T_DEFAULT +%token T_TRISTATE +%token T_DEF_TRISTATE +%token T_BOOLEAN +%token T_DEF_BOOLEAN +%token T_STRING +%token T_INT +%token T_HEX +%token T_WORD +%token T_WORD_QUOTE +%token T_UNEQUAL +%token T_EOF +%token T_EOL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_ON +%token T_SELECT +%token T_RANGE + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%nonassoc T_NOT + +%type prompt +%type source +%type symbol +%type expr +%type if_expr +%type end + +%{ +#define LKC_DIRECT_LINK +#include "lkc.h" +%} +%% +input: /* empty */ + | input block +; + +block: common_block + | choice_stmt + | menu_stmt + | T_MAINMENU prompt nl_or_eof + | T_ENDMENU { zconfprint("unexpected 'endmenu' statement"); } + | T_ENDIF { zconfprint("unexpected 'endif' statement"); } + | T_ENDCHOICE { zconfprint("unexpected 'endchoice' statement"); } + | error nl_or_eof { zconfprint("syntax error"); yyerrok; } +; + +common_block: + if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt + | nl_or_eof +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list depends + | config_option_list help + | config_option_list T_EOL +; + +config_option: T_TRISTATE prompt_stmt_opt T_EOL +{ + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEF_TRISTATE expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_BOOLEAN prompt_stmt_opt T_EOL +{ + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEF_BOOLEAN expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_INT prompt_stmt_opt T_EOL +{ + menu_set_type(S_INT); + printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_HEX prompt_stmt_opt T_EOL +{ + menu_set_type(S_HEX); + printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_STRING prompt_stmt_opt T_EOL +{ + menu_set_type(S_STRING); + printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +/* choice entry */ + +choice: T_CHOICE T_EOL +{ + struct symbol *sym = sym_lookup(NULL, 0); + sym->flags |= SYMBOL_CHOICE; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + menu_end_entry(); + menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: + choice_entry choice_block choice_end + | choice_entry choice_block +{ + printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TRISTATE prompt_stmt_opt T_EOL +{ + menu_set_type(S_TRISTATE); + printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_BOOLEAN prompt_stmt_opt T_EOL +{ + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno()); +}; + +choice_block: + /* empty */ + | choice_block common_block +; + +/* if entry */ + +if: T_IF expr T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + menu_end_entry(); + menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: + if if_block if_end + | if if_block +{ + printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +if_block: + /* empty */ + | if_block common_block + | if_block menu_stmt + | if_block choice_stmt +; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prop(P_MENU, $2, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu depends_list +{ + menu_end_entry(); + menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: + menu_entry menu_block menu_end + | menu_entry menu_block +{ + printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno); + zconfnerrs++; +}; + +menu_block: + /* empty */ + | menu_block common_block + | menu_block menu_stmt + | menu_block choice_stmt + | menu_block error T_EOL { zconfprint("invalid menu option"); yyerrok; } +; + +source: T_SOURCE prompt T_EOL +{ + $$ = $2; + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +source_stmt: source +{ + zconf_nextfile($1); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prop(P_COMMENT, $2, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->sym->help = $2; +}; + +/* depends option */ + +depends_list: /* empty */ + | depends_list depends + | depends_list T_EOL +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +} + | T_DEPENDS expr T_EOL +{ + menu_add_dep($2); + printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno()); +} + | T_REQUIRES expr T_EOL +{ + menu_add_dep($2); + printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno()); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prop(P_PROMPT, $1, NULL, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU nl_or_eof { $$ = T_ENDMENU; } + | T_ENDCHOICE nl_or_eof { $$ = T_ENDCHOICE; } + | T_ENDIF nl_or_eof { $$ = T_ENDIF; } +; + +nl_or_eof: + T_EOL | T_EOF; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); } +; + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + menu_init(); + modules_sym = sym_lookup("MODULES", 0); + rootmenu.prompt = menu_add_prop(P_MENU, "axTLS Configuration", NULL, NULL); + + //zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym)) + printf("\n"); + else + sym->flags |= SYMBOL_CHECK_DONE; + } + + sym_change_count = 1; +} + +const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + } + return ""; +} + +static bool zconf_endtoken(int token, int starttoken, int endtoken) +{ + if (token != endtoken) { + zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken)); + zconfprint("location of the '%s'", zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "choice\n"); + else + fprintf(out, "config %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (sym->help) { + int len = strlen(sym->help); + while (sym->help[--len] == '\n') + sym->help[len] = 0; + fprintf(out, " help\n%s\n", sym->help); + } + fputc('\n', out); +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + fputs("\n", out); + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "lex.zconf.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" diff --git a/libs/nixio/axTLS/config/win32config b/libs/nixio/axTLS/config/win32config new file mode 100644 index 000000000..6c8d60712 --- /dev/null +++ b/libs/nixio/axTLS/config/win32config @@ -0,0 +1,119 @@ +# +# Automatically generated make config: don't edit +# +HAVE_DOT_CONFIG=y +# CONFIG_PLATFORM_LINUX is not set +# CONFIG_PLATFORM_CYGWIN is not set +# CONFIG_PLATFORM_SOLARIS is not set +CONFIG_PLATFORM_WIN32=y + +# +# General Configuration +# +PREFIX="" +# CONFIG_DEBUG is not set +# CONFIG_STRIP_UNWANTED_SECTIONS is not set + +# +# Microsoft Compiler Options +# +# CONFIG_VISUAL_STUDIO_7_0 is not set +CONFIG_VISUAL_STUDIO_8_0=y +CONFIG_VISUAL_STUDIO_7_0_BASE="" +CONFIG_VISUAL_STUDIO_8_0_BASE="c:\\Program Files\\Microsoft Visual Studio 8" +CONFIG_EXTRA_CFLAGS_OPTIONS="" +CONFIG_EXTRA_LDFLAGS_OPTIONS="" + +# +# SSL Library +# +# CONFIG_SSL_SERVER_ONLY is not set +# CONFIG_SSL_CERT_VERIFICATION is not set +# CONFIG_SSL_ENABLE_CLIENT is not set +CONFIG_SSL_FULL_MODE=y +# CONFIG_SSL_SKELETON_MODE is not set +# CONFIG_SSL_PROT_LOW is not set +CONFIG_SSL_PROT_MEDIUM=y +# CONFIG_SSL_PROT_HIGH is not set +CONFIG_SSL_USE_DEFAULT_KEY=y +CONFIG_SSL_PRIVATE_KEY_LOCATION="" +CONFIG_SSL_PRIVATE_KEY_PASSWORD="" +CONFIG_SSL_X509_CERT_LOCATION="" +CONFIG_SSL_GENERATE_X509_CERT=y +CONFIG_SSL_X509_COMMON_NAME="" +CONFIG_SSL_X509_ORGANIZATION_NAME="" +CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME="" +CONFIG_SSL_ENABLE_V23_HANDSHAKE=y +CONFIG_SSL_HAS_PEM=y +CONFIG_SSL_USE_PKCS12=y +CONFIG_SSL_EXPIRY_TIME=24 +CONFIG_X509_MAX_CA_CERTS=4 +CONFIG_SSL_MAX_CERTS=2 +# CONFIG_SSL_CTX_MUTEXING is not set +# CONFIG_USE_DEV_URANDOM is not set +CONFIG_WIN32_USE_CRYPTO_LIB=y +# CONFIG_OPENSSL_COMPATIBLE is not set +# CONFIG_PERFORMANCE_TESTING is not set +# CONFIG_SSL_TEST is not set +CONFIG_AXHTTPD=y + +# +# Axhttpd Configuration +# +# CONFIG_HTTP_STATIC_BUILD is not set +CONFIG_HTTP_PORT=80 +CONFIG_HTTP_HTTPS_PORT=443 +CONFIG_HTTP_SESSION_CACHE_SIZE=5 +CONFIG_HTTP_WEBROOT="www" +CONFIG_HTTP_TIMEOUT=300 +# CONFIG_HTTP_HAS_CGI is not set +CONFIG_HTTP_CGI_EXTENSIONS="" +# CONFIG_HTTP_ENABLE_LUA is not set +CONFIG_HTTP_LUA_PREFIX="" +CONFIG_HTTP_LUA_CGI_LAUNCHER="" +# CONFIG_HTTP_BUILD_LUA is not set +CONFIG_HTTP_DIRECTORIES=y +CONFIG_HTTP_HAS_AUTHORIZATION=y +# CONFIG_HTTP_HAS_IPV6 is not set +CONFIG_HTTP_VERBOSE=y +# CONFIG_HTTP_IS_DAEMON is not set + +# +# Language Bindings +# +# CONFIG_BINDINGS is not set +# CONFIG_CSHARP_BINDINGS is not set +# CONFIG_VBNET_BINDINGS is not set +CONFIG_DOT_NET_FRAMEWORK_BASE="" +# CONFIG_JAVA_BINDINGS is not set +CONFIG_JAVA_HOME="" +# CONFIG_PERL_BINDINGS is not set +CONFIG_PERL_CORE="" +CONFIG_PERL_LIB="" +# CONFIG_LUA_BINDINGS is not set +CONFIG_LUA_CORE="" + +# +# Samples +# +CONFIG_SAMPLES=y +CONFIG_C_SAMPLES=y +# CONFIG_CSHARP_SAMPLES is not set +# CONFIG_VBNET_SAMPLES is not set +# CONFIG_JAVA_SAMPLES is not set +# CONFIG_PERL_SAMPLES is not set +# CONFIG_LUA_SAMPLES is not set + +# +# BigInt Options +# +# CONFIG_BIGINT_CLASSICAL is not set +# CONFIG_BIGINT_MONTGOMERY is not set +CONFIG_BIGINT_BARRETT=y +CONFIG_BIGINT_CRT=y +# CONFIG_BIGINT_KARATSUBA is not set +MUL_KARATSUBA_THRESH=0 +SQU_KARATSUBA_THRESH=0 +CONFIG_BIGINT_SLIDING_WINDOW=y +CONFIG_BIGINT_SQUARE=y +# CONFIG_BIGINT_CHECK_ON is not set diff --git a/libs/nixio/axTLS/crypto/.depend b/libs/nixio/axTLS/crypto/.depend new file mode 100644 index 000000000..4fb04339c --- /dev/null +++ b/libs/nixio/axTLS/crypto/.depend @@ -0,0 +1,19 @@ +aes.o: aes.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +bigint.o: bigint.c bigint.h crypto.h ../config/config.h ../ssl/os_port.h \ + bigint_impl.h +crypto_misc.o: crypto_misc.c ../ssl/crypto_misc.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h ../crypto/bigint.h +hmac.o: hmac.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +md2.o: md2.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +md5.o: md5.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +rc4.o: rc4.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +rsa.o: rsa.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h +sha1.o: sha1.c crypto.h ../config/config.h ../ssl/os_port.h bigint_impl.h \ + bigint.h diff --git a/libs/nixio/axTLS/crypto/Makefile b/libs/nixio/axTLS/crypto/Makefile new file mode 100644 index 000000000..3ea8bdde0 --- /dev/null +++ b/libs/nixio/axTLS/crypto/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../config/.config +include ../config/makefile.conf + +AXTLS_HOME=.. + +OBJ=\ + aes.o \ + bigint.o \ + crypto_misc.o \ + hmac.o \ + md2.o \ + md5.o \ + rc4.o \ + rsa.o \ + sha1.o + +include ../config/makefile.post + +all: $(OBJ) + diff --git a/libs/nixio/axTLS/crypto/aes.c b/libs/nixio/axTLS/crypto/aes.c new file mode 100644 index 000000000..038a45bd8 --- /dev/null +++ b/libs/nixio/axTLS/crypto/aes.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * AES implementation - this is a small code version. There are much faster + * versions around but they are much larger in size (i.e. they use large + * submix tables). + */ + +#include +#include "crypto.h" + +/* all commented out in skeleton mode */ +#ifndef CONFIG_SSL_SKELETON_MODE + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) << 8) | ((x) >> 24)) + +/* + * This cute trick does 4 'mul by two' at once. Stolen from + * Dr B. R. Gladman but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, + * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, + * c 0000 0001 0000 0000 we then subtract (c) from (b) + * d 0111 1111 0000 0000 and now we and with our mask + * e 0001 1011 0000 0000 + */ +#define mt 0x80808080 +#define ml 0x7f7f7f7f +#define mh 0xfefefefe +#define mm 0x1b1b1b1b +#define mul2(x,t) ((t)=((x)&mt), \ + ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ + (f2)=mul2(x,f2), \ + (f4)=mul2(f2,f4), \ + (f8)=mul2(f4,f8), \ + (f9)=(x)^(f8), \ + (f8)=((f2)^(f4)^(f8)), \ + (f2)^=(f9), \ + (f4)^=(f9), \ + (f8)^=rot3(f2), \ + (f8)^=rot2(f4), \ + (f8)^rot1(f9)) + +/* + * AES S-box + */ +static const uint8_t aes_sbox[256] = +{ + 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, + 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, + 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, + 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, + 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, + 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, + 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, + 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, + 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, + 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, + 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, + 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, + 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, + 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, + 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, + 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, + 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, +}; + +/* + * AES is-box + */ +static const uint8_t aes_isbox[256] = +{ + 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, + 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, + 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, + 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, + 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, + 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, + 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, + 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, + 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, + 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, + 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, + 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, + 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, + 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, + 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, + 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, + 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, + 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, + 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, + 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, + 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, + 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, + 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, + 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, + 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, + 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, + 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, + 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, + 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, + 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, + 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, + 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d +}; + +static const unsigned char Rcon[30]= +{ + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, + 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, + 0xb3,0x7d,0xfa,0xef,0xc5,0x91, +}; + +/* ----- static functions ----- */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); + +/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial + x^8+x^4+x^3+x+1 */ +static unsigned char AES_xtime(uint32_t x) +{ + return x = (x&0x80) ? (x<<1)^0x1b : x<<1; +} + +/** + * Set up AES with the key/iv and cipher size. + */ +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode) +{ + int i, ii; + uint32_t *W, tmp, tmp2; + const unsigned char *ip; + int words; + + switch (mode) + { + case AES_MODE_128: + i = 10; + words = 4; + break; + + case AES_MODE_256: + i = 14; + words = 8; + break; + + default: /* fail silently */ + return; + } + + ctx->rounds = i; + ctx->key_size = words; + W = ctx->ks; + for (i = 0; i < words; i+=2) + { + W[i+0]= ((uint32_t)key[ 0]<<24)| + ((uint32_t)key[ 1]<<16)| + ((uint32_t)key[ 2]<< 8)| + ((uint32_t)key[ 3] ); + W[i+1]= ((uint32_t)key[ 4]<<24)| + ((uint32_t)key[ 5]<<16)| + ((uint32_t)key[ 6]<< 8)| + ((uint32_t)key[ 7] ); + key += 8; + } + + ip = Rcon; + ii = 4 * (ctx->rounds+1); + for (i = words; i> 8)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; + tmp=tmp2^(((unsigned int)*ip)<<24); + ip++; + } + + if ((words == 8) && ((i % words) == 4)) + { + tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; + tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; + tmp=tmp2; + } + + W[i]=W[i-words]^tmp; + } + + /* copy the iv across */ + memcpy(ctx->iv, iv, 16); +} + +/** + * Change a key for decryption. + */ +void AES_convert_key(AES_CTX *ctx) +{ + int i; + uint32_t *k,w,t1,t2,t3,t4; + + k = ctx->ks; + k += 4; + + for (i= ctx->rounds*4; i > 4; i--) + { + w= *k; + w = inv_mix_col(w,t1,t2,t3,t4); + *k++ =w; + } +} + +/** + * Encrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], tout[4], iv[4]; + + memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + tout[i] = ntohl(iv[i]); + + for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + tin[i] = ntohl(msg_32[i])^tout[i]; + + AES_encrypt(ctx, tin); + + for (i = 0; i < 4; i++) + { + tout[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(tout[i]); + memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Decrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; + + memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + xor[i] = ntohl(iv[i]); + + for (length -= 16; length >= 0; length -= 16) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + { + tin[i] = ntohl(msg_32[i]); + data[i] = tin[i]; + } + + AES_decrypt(ctx, data); + + for (i = 0; i < 4; i++) + { + tout[i] = data[i]^xor[i]; + xor[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(xor[i]); + memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Encrypt a single block (16 bytes) of data + */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data) +{ + /* To make this code smaller, generate the sbox entries on the fly. + * This will have a really heavy effect upon performance. + */ + uint32_t tmp[4]; + uint32_t tmp1, old_a0, a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks; + + /* Pre-round key addition */ + for (row = 0; row < 4; row++) + data[row] ^= *(k++); + + /* Encrypt one block. */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 0; row < 4; row++) + { + a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; + a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; + a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; + a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd < (rounds - 1)) + { + tmp1 = a0 ^ a1 ^ a2 ^ a3; + old_a0 = a0; + a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); + a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); + a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); + a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); + } + + tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); + } + + /* KeyAddition - note that it is vital that this loop is separate from + the MixColumn operation, which must be atomic...*/ + for (row = 0; row < 4; row++) + data[row] = tmp[row] ^ *(k++); + } +} + +/** + * Decrypt a single block (16 bytes) of data + */ +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data) +{ + uint32_t tmp[4]; + uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; + uint32_t a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks + ((rounds+1)*4); + + /* pre-round key addition */ + for (row=4; row > 0;row--) + data[row-1] ^= *(--k); + + /* Decrypt one block */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 4; row > 0; row--) + { + a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; + a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; + a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; + a3 = aes_isbox[(data[row%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd<(rounds-1)) + { + /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) + are quite large compared to encryption; this + operation slows decryption down noticeably. */ + xt0 = AES_xtime(a0^a1); + xt1 = AES_xtime(a1^a2); + xt2 = AES_xtime(a2^a3); + xt3 = AES_xtime(a3^a0); + xt4 = AES_xtime(xt0^xt1); + xt5 = AES_xtime(xt1^xt2); + xt6 = AES_xtime(xt4^xt5); + + xt0 ^= a1^a2^a3^xt4^xt6; + xt1 ^= a0^a2^a3^xt5^xt6; + xt2 ^= a0^a1^a3^xt4^xt6; + xt3 ^= a0^a1^a2^xt5^xt6; + tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); + } + else + tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); + } + + for (row = 4; row > 0; row--) + data[row-1] = tmp[row-1] ^ *(--k); + } +} + +#endif diff --git a/libs/nixio/axTLS/crypto/bigint.c b/libs/nixio/axTLS/crypto/bigint.c new file mode 100644 index 000000000..53a583929 --- /dev/null +++ b/libs/nixio/axTLS/crypto/bigint.c @@ -0,0 +1,1575 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @defgroup bigint_api Big Integer API + * @brief The bigint implementation as used by the axTLS project. + * + * The bigint library is for RSA encryption/decryption as well as signing. + * This code tries to minimise use of malloc/free by maintaining a small + * cache. A bigint context may maintain state by being made "permanent". + * It be be later released with a bi_depermanent() and bi_free() call. + * + * It supports the following reduction techniques: + * - Classical + * - Barrett + * - Montgomery + * + * It also implements the following: + * - Karatsuba multiplication + * - Squaring + * - Sliding window exponentiation + * - Chinese Remainder Theorem (implemented in rsa.c). + * + * All the algorithms used are pretty standard, and designed for different + * data bus sizes. Negative numbers are not dealt with at all, so a subtraction + * may need to be tested for negativity. + * + * This library steals some ideas from Jef Poskanzer + * + * and GMP . It gets most of its implementation + * detail from "The Handbook of Applied Cryptography" + * + * @{ + */ + +#include +#include +#include +#include +#include +#include "bigint.h" + +#define V1 v->comps[v->size-1] /**< v1 for division */ +#define V2 v->comps[v->size-2] /**< v2 for division */ +#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */ +#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */ + +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i); +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom); +static bigint *alloc(BI_CTX *ctx, int size); +static bigint *trim(bigint *bi); +static void more_comps(bigint *bi, int n); +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +static bigint *comp_right_shift(bigint *biR, int num_shifts); +static bigint *comp_left_shift(bigint *biR, int num_shifts); +#endif + +#ifdef CONFIG_BIGINT_CHECK_ON +static void check(const bigint *bi); +#else +#define check(A) /**< disappears in normal production mode */ +#endif + + +/** + * @brief Start a new bigint context. + * @return A bigint context. + */ +BI_CTX *bi_initialize(void) +{ + /* calloc() sets everything to zero */ + BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX)); + + /* the radix */ + ctx->bi_radix = alloc(ctx, 2); + ctx->bi_radix->comps[0] = 0; + ctx->bi_radix->comps[1] = 1; + bi_permanent(ctx->bi_radix); + return ctx; +} + +/** + * @brief Close the bigint context and free any resources. + * + * Free up any used memory - a check is done if all objects were not + * properly freed. + * @param ctx [in] The bigint session context. + */ +void bi_terminate(BI_CTX *ctx) +{ + bi_depermanent(ctx->bi_radix); + bi_free(ctx, ctx->bi_radix); + + if (ctx->active_count != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_terminate: there were %d un-freed bigints\n", + ctx->active_count); +#endif + abort(); + } + + bi_clear_cache(ctx); + free(ctx); +} + +/** + *@brief Clear the memory cache. + */ +void bi_clear_cache(BI_CTX *ctx) +{ + bigint *p, *pn; + + if (ctx->free_list == NULL) + return; + + for (p = ctx->free_list; p != NULL; p = pn) + { + pn = p->next; + free(p->comps); + free(p); + } + + ctx->free_count = 0; + ctx->free_list = NULL; +} + +/** + * @brief Increment the number of references to this object. + * It does not do a full copy. + * @param bi [in] The bigint to copy. + * @return A reference to the same bigint. + */ +bigint *bi_copy(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + bi->refs++; + return bi; +} + +/** + * @brief Simply make a bigint object "unfreeable" if bi_free() is called on it. + * + * For this object to be freed, bi_depermanent() must be called. + * @param bi [in] The bigint to be made permanent. + */ +void bi_permanent(bigint *bi) +{ + check(bi); + if (bi->refs != 1) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_permanent: refs was not 1\n"); +#endif + abort(); + } + + bi->refs = PERMANENT; +} + +/** + * @brief Take a permanent object and make it eligible for freedom. + * @param bi [in] The bigint to be made back to temporary. + */ +void bi_depermanent(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_depermanent: bigint was not permanent\n"); +#endif + abort(); + } + + bi->refs = 1; +} + +/** + * @brief Free a bigint object so it can be used again. + * + * The memory itself it not actually freed, just tagged as being available + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to be freed. + */ +void bi_free(BI_CTX *ctx, bigint *bi) +{ + check(bi); + if (bi->refs == PERMANENT) + { + return; + } + + if (--bi->refs > 0) + { + return; + } + + bi->next = ctx->free_list; + ctx->free_list = bi; + ctx->free_count++; + + if (--ctx->active_count < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_free: active_count went negative " + "- double-freed bigint?\n"); +#endif + abort(); + } +} + +/** + * @brief Convert an (unsigned) integer into a bigint. + * @param ctx [in] The bigint session context. + * @param i [in] The (unsigned) integer to be converted. + * + */ +bigint *int_to_bi(BI_CTX *ctx, comp i) +{ + bigint *biR = alloc(ctx, 1); + biR->comps[0] = i; + return biR; +} + +/** + * @brief Do a full copy of the bigint object. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint object to be copied. + */ +bigint *bi_clone(BI_CTX *ctx, const bigint *bi) +{ + bigint *biR = alloc(ctx, bi->size); + check(bi); + memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE); + return biR; +} + +/** + * @brief Perform an addition operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the addition. + */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int n; + comp carry = 0; + comp *pa, *pb; + + check(bia); + check(bib); + + n = max(bia->size, bib->size); + more_comps(bia, n+1); + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa + *pb++; + rl = sl + carry; + cy1 = sl < *pa; + carry = cy1 | (rl < sl); + *pa++ = rl; + } while (--n != 0); + + *pa = carry; /* do overflow */ + bi_free(ctx, bib); + return trim(bia); +} + +/** + * @brief Perform a subtraction operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @param is_negative [out] If defined, indicates that the result was negative. + * is_negative may be null. + * @return The result of the subtraction. The result is always positive. + */ +bigint *bi_subtract(BI_CTX *ctx, + bigint *bia, bigint *bib, int *is_negative) +{ + int n = bia->size; + comp *pa, *pb, carry = 0; + + check(bia); + check(bib); + + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa - *pb++; + rl = sl - carry; + cy1 = sl > *pa; + carry = cy1 | (rl > sl); + *pa++ = rl; + } while (--n != 0); + + if (is_negative) /* indicate a negative result */ + { + *is_negative = carry; + } + + bi_free(ctx, trim(bib)); /* put bib back to the way it was */ + return trim(bia); +} + +/** + * Perform a multiply between a bigint an an (unsigned) integer + */ +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b) +{ + int j = 0, n = bia->size; + bigint *biR = alloc(ctx, n + 1); + comp carry = 0; + comp *r = biR->comps; + comp *a = bia->comps; + + check(bia); + + /* clear things to start with */ + memset(r, 0, ((n+1)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp = *r + (long_comp)a[j]*b + carry; + *r++ = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + *r = carry; + bi_free(ctx, bia); + return trim(biR); +} + +/** + * @brief Does both division and modulo calculations. + * + * Used extensively when doing classical reduction. + * @param ctx [in] The bigint session context. + * @param u [in] A bigint which is the numerator. + * @param v [in] Either the denominator or the modulus depending on the mode. + * @param is_mod [n] Determines if this is a normal division (0) or a reduction + * (1). + * @return The result of the division/reduction. + */ +bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) +{ + int n = v->size, m = u->size-n; + int j = 0, orig_u_size = u->size; + uint8_t mod_offset = ctx->mod_offset; + comp d; + bigint *quotient, *tmp_u; + comp q_dash; + + check(u); + check(v); + + /* if doing reduction and we are < mod, then return mod */ + if (is_mod && bi_compare(v, u) > 0) + { + bi_free(ctx, v); + return u; + } + + quotient = alloc(ctx, m+1); + tmp_u = alloc(ctx, n+1); + v = trim(v); /* make sure we have no leading 0's */ + d = (comp)((long_comp)COMP_RADIX/(V1+1)); + + /* clear things to start with */ + memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE)); + + /* normalise */ + if (d > 1) + { + u = bi_int_multiply(ctx, u, d); + + if (is_mod) + { + v = ctx->bi_normalised_mod[mod_offset]; + } + else + { + v = bi_int_multiply(ctx, v, d); + } + } + + if (orig_u_size == u->size) /* new digit position u0 */ + { + more_comps(u, orig_u_size + 1); + } + + do + { + /* get a temporary short version of u */ + memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE); + + /* calculate q' */ + if (U(0) == V1) + { + q_dash = COMP_RADIX-1; + } + else + { + q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1); + } + + if (v->size > 1 && V2) + { + /* we are implementing the following: + if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) - + q_dash*V1)*COMP_RADIX) + U(2))) ... */ + comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) - + (long_comp)q_dash*V1); + if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2)) + { + q_dash--; + } + } + + /* multiply and subtract */ + if (q_dash) + { + int is_negative; + tmp_u = bi_subtract(ctx, tmp_u, + bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative); + more_comps(tmp_u, n+1); + + Q(j) = q_dash; + + /* add back */ + if (is_negative) + { + Q(j)--; + tmp_u = bi_add(ctx, tmp_u, bi_copy(v)); + + /* lop off the carry */ + tmp_u->size--; + v->size--; + } + } + else + { + Q(j) = 0; + } + + /* copy back to u */ + memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE); + } while (++j <= m); + + bi_free(ctx, tmp_u); + bi_free(ctx, v); + + if (is_mod) /* get the remainder */ + { + bi_free(ctx, quotient); + return bi_int_divide(ctx, trim(u), d); + } + else /* get the quotient */ + { + bi_free(ctx, u); + return trim(quotient); + } +} + +/* + * Perform an integer divide on a bigint. + */ +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom) +{ + int i = biR->size - 1; + long_comp r = 0; + + check(biR); + + do + { + r = (r<comps[i]; + biR->comps[i] = (comp)(r / denom); + r %= denom; + } while (--i >= 0); + + return trim(biR); +} + +#ifdef CONFIG_BIGINT_MONTGOMERY +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ +static comp modular_inverse(bigint *bim) +{ + int i; + comp t = 1; + comp two_2_i_minus_1 = 2; /* 2^(i-1) */ + long_comp two_2_i = 4; /* 2^i */ + comp N = bim->comps[0]; + + for (i = 2; i <= COMP_BIT_SIZE; i++) + { + if ((long_comp)N*t%two_2_i >= two_2_i_minus_1) + { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (comp)(COMP_RADIX-t); +} +#endif + +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +/** + * Take each component and shift down (in terms of components) + */ +static bigint *comp_right_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-num_shifts; + comp *x = biR->comps; + comp *y = &biR->comps[num_shifts]; + + check(biR); + + if (i <= 0) /* have we completely right shifted? */ + { + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + do + { + *x++ = *y++; + } while (--i > 0); + + biR->size -= num_shifts; + return biR; +} + +/** + * Take each component and shift it up (in terms of components) + */ +static bigint *comp_left_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-1; + comp *x, *y; + + check(biR); + + if (num_shifts <= 0) + { + return biR; + } + + more_comps(biR, biR->size + num_shifts); + + x = &biR->comps[i+num_shifts]; + y = &biR->comps[i]; + + do + { + *x-- = *y--; + } while (i--); + + memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */ + return biR; +} +#endif + +/** + * @brief Allow a binary sequence to be imported as a bigint. + * @param ctx [in] The bigint session context. + * @param data [in] The data to be converted. + * @param size [in] The number of bytes of data. + * @return A bigint representing this data. + */ +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int size) +{ + bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE); + int i, j = 0, offset = 0; + + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + biR->comps[offset] += data[i] << (j*8); + + if (++j == COMP_BYTE_SIZE) + { + j = 0; + offset ++; + } + } + + return trim(biR); +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * @brief The testharness uses this code to import text hex-streams and + * convert them into bigints. + * @param ctx [in] The bigint session context. + * @param data [in] A string consisting of hex characters. The characters must + * be in upper case. + * @return A bigint representing this data. + */ +bigint *bi_str_import(BI_CTX *ctx, const char *data) +{ + int size = strlen(data); + bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES); + int i, j = 0, offset = 0; + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10); + biR->comps[offset] += num << (j*4); + + if (++j == COMP_NUM_NIBBLES) + { + j = 0; + offset ++; + } + } + + return biR; +} + +void bi_print(const char *label, bigint *x) +{ + int i, j; + + if (x == NULL) + { + printf("%s: (null)\n", label); + return; + } + + printf("%s: (size %d)\n", label, x->size); + for (i = x->size-1; i >= 0; i--) + { + for (j = COMP_NUM_NIBBLES-1; j >= 0; j--) + { + comp mask = 0x0f << (j*4); + comp num = (x->comps[i] & mask) >> (j*4); + putc((num <= 9) ? (num + '0') : (num + 'A' - 10), stdout); + } + } + + printf("\n"); +} +#endif + +/** + * @brief Take a bigint and convert it into a byte sequence. + * + * This is useful after a decrypt operation. + * @param ctx [in] The bigint session context. + * @param x [in] The bigint to be converted. + * @param data [out] The converted data as a byte stream. + * @param size [in] The maximum size of the byte stream. Unused bytes will be + * zeroed. + */ +void bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size) +{ + int i, j, k = size-1; + + check(x); + memset(data, 0, size); /* ensure all leading 0's are cleared */ + + for (i = 0; i < x->size; i++) + { + for (j = 0; j < COMP_BYTE_SIZE; j++) + { + comp mask = 0xff << (j*8); + int num = (x->comps[i] & mask) >> (j*8); + data[k--] = num; + + if (k < 0) + { + break; + } + } + } + + bi_free(ctx, x); +} + +/** + * @brief Pre-calculate some of the expensive steps in reduction. + * + * This function should only be called once (normally when a session starts). + * When the session is over, bi_free_mod() should be called. bi_mod_power() + * relies on this function being called. + * @param ctx [in] The bigint session context. + * @param bim [in] The bigint modulus that will be used. + * @param mod_offset [in] There are three moduluii that can be stored - the + * standard modulus, and its two primes p and q. This offset refers to which + * modulus we are referring to. + * @see bi_free_mod(), bi_mod_power(). + */ +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset) +{ + int k = bim->size; + comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1)); +#ifdef CONFIG_BIGINT_MONTGOMERY + bigint *R, *R2; +#endif + + ctx->bi_mod[mod_offset] = bim; + bi_permanent(ctx->bi_mod[mod_offset]); + ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d); + bi_permanent(ctx->bi_normalised_mod[mod_offset]); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + /* set montgomery variables */ + R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */ + R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */ + ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */ + ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */ + + bi_permanent(ctx->bi_RR_mod_m[mod_offset]); + bi_permanent(ctx->bi_R_mod_m[mod_offset]); + + ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]); + +#elif defined (CONFIG_BIGINT_BARRETT) + ctx->bi_mu[mod_offset] = + bi_divide(ctx, comp_left_shift( + bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0); + bi_permanent(ctx->bi_mu[mod_offset]); +#endif +} + +/** + * @brief Used when cleaning various bigints at the end of a session. + * @param ctx [in] The bigint session context. + * @param mod_offset [in] The offset to use. + * @see bi_set_mod(). + */ +void bi_free_mod(BI_CTX *ctx, int mod_offset) +{ + bi_depermanent(ctx->bi_mod[mod_offset]); + bi_free(ctx, ctx->bi_mod[mod_offset]); +#if defined (CONFIG_BIGINT_MONTGOMERY) + bi_depermanent(ctx->bi_RR_mod_m[mod_offset]); + bi_depermanent(ctx->bi_R_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_R_mod_m[mod_offset]); +#elif defined(CONFIG_BIGINT_BARRETT) + bi_depermanent(ctx->bi_mu[mod_offset]); + bi_free(ctx, ctx->bi_mu[mod_offset]); +#endif + bi_depermanent(ctx->bi_normalised_mod[mod_offset]); + bi_free(ctx, ctx->bi_normalised_mod[mod_offset]); +} + +/** + * Perform a standard multiplication between two bigints. + */ +static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int i, j, i_plus_j; + int n = bia->size; + int t = bib->size; + bigint *biR = alloc(ctx, n + t); + comp *sr = biR->comps; + comp *sa = bia->comps; + comp *sb = bib->comps; + + check(bia); + check(bib); + + /* clear things to start with */ + memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE)); + i = 0; + + do + { + comp carry = 0; + comp b = *sb++; + i_plus_j = i; + j = 0; + + do + { + long_comp tmp = sr[i_plus_j] + (long_comp)sa[j]*b + carry; + sr[i_plus_j++] = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + sr[i_plus_j] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +#ifdef CONFIG_BIGINT_KARATSUBA +/* + * Karatsuba improves on regular multiplication due to only 3 multiplications + * being done instead of 4. The additional additions/subtractions are O(N) + * rather than O(N^2) and so for big numbers it saves on a few operations + */ +static bigint *karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square) +{ + bigint *x0, *x1; + bigint *p0, *p1, *p2; + int m; + + if (is_square) + { + m = (bia->size + 1)/2; + } + else + { + m = (max(bia->size, bib->size) + 1)/2; + } + + x0 = bi_clone(ctx, bia); + x0->size = m; + x1 = bi_clone(ctx, bia); + comp_right_shift(x1, m); + bi_free(ctx, bia); + + /* work out the 3 partial products */ + if (is_square) + { + p0 = bi_square(ctx, bi_copy(x0)); + p2 = bi_square(ctx, bi_copy(x1)); + p1 = bi_square(ctx, bi_add(ctx, x0, x1)); + } + else /* normal multiply */ + { + bigint *y0, *y1; + y0 = bi_clone(ctx, bib); + y0->size = m; + y1 = bi_clone(ctx, bib); + comp_right_shift(y1, m); + bi_free(ctx, bib); + + p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0)); + p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1)); + p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1)); + } + + p1 = bi_subtract(ctx, + bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL); + + comp_left_shift(p1, m); + comp_left_shift(p2, 2*m); + return bi_add(ctx, p1, bi_add(ctx, p0, p2)); +} +#endif + +/** + * @brief Perform a multiplication operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the multiplication. + */ +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + check(bia); + check(bib); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH) + { + return regular_multiply(ctx, bia, bib); + } + + return karatsuba(ctx, bia, bib, 0); +#else + return regular_multiply(ctx, bia, bib); +#endif +} + +#ifdef CONFIG_BIGINT_SQUARE +/* + * Perform the actual square operion. It takes into account overflow. + */ +static bigint *regular_square(BI_CTX *ctx, bigint *bi) +{ + int t = bi->size; + int i = 0, j; + bigint *biR = alloc(ctx, t*2); + comp *w = biR->comps; + comp *x = bi->comps; + comp carry; + + memset(w, 0, biR->size*COMP_BYTE_SIZE); + + do + { + long_comp tmp = w[2*i] + (long_comp)x[i]*x[i]; + comp u = 0; + w[2*i] = (comp)tmp; + carry = (comp)(tmp >> COMP_BIT_SIZE); + + for (j = i+1; j < t; j++) + { + long_comp xx = (long_comp)x[i]*x[j]; + long_comp xx2 = 2*xx; + long_comp blob = (long_comp)w[i+j]+carry; + + if (u) /* previous overflow */ + { + blob += COMP_RADIX; + } + + + u = 0; + tmp = xx2 + blob; + + /* check for overflow */ + if ((COMP_MAX-xx) < xx || (COMP_MAX-xx2) < blob) + { + u = 1; + } + + w[i+j] = (comp)tmp; + carry = (comp)(tmp >> COMP_BIT_SIZE); + } + + w[i+t] += carry; + + if (u) + { + w[i+t+1] = 1; /* add carry */ + } + } while (++i < t); + + bi_free(ctx, bi); + return trim(biR); +} + +/** + * @brief Perform a square operation on a bigint. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @return The result of the multiplication. + */ +bigint *bi_square(BI_CTX *ctx, bigint *bia) +{ + check(bia); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (bia->size < SQU_KARATSUBA_THRESH) + { + return regular_square(ctx, bia); + } + + return karatsuba(ctx, bia, NULL, 1); +#else + return regular_square(ctx, bia); +#endif +} +#endif + +/** + * @brief Compare two bigints. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return -1 if smaller, 1 if larger and 0 if equal. + */ +int bi_compare(bigint *bia, bigint *bib) +{ + int r, i; + + check(bia); + check(bib); + + if (bia->size > bib->size) + r = 1; + else if (bia->size < bib->size) + r = -1; + else + { + comp *a = bia->comps; + comp *b = bib->comps; + + /* Same number of components. Compare starting from the high end + * and working down. */ + r = 0; + i = bia->size - 1; + + do + { + if (a[i] > b[i]) + { + r = 1; + break; + } + else if (a[i] < b[i]) + { + r = -1; + break; + } + } while (--i >= 0); + } + + return r; +} + +/* + * Allocate and zero more components. Does not consume bi. + */ +static void more_comps(bigint *bi, int n) +{ + if (n > bi->max_comps) + { + bi->max_comps = max(bi->max_comps * 2, n); + bi->comps = (comp*)realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE); + } + + if (n > bi->size) + { + memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE); + } + + bi->size = n; +} + +/* + * Make a new empty bigint. It may just use an old one if one is available. + * Otherwise get one off the heap. + */ +static bigint *alloc(BI_CTX *ctx, int size) +{ + bigint *biR; + + /* Can we recycle an old bigint? */ + if (ctx->free_list != NULL) + { + biR = ctx->free_list; + ctx->free_list = biR->next; + ctx->free_count--; + + if (biR->refs != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("alloc: refs was not 0\n"); +#endif + abort(); /* create a stack trace from a core dump */ + } + + more_comps(biR, size); + } + else + { + /* No free bigints available - create a new one. */ + biR = (bigint *)malloc(sizeof(bigint)); + biR->comps = (comp*)malloc(size * COMP_BYTE_SIZE); + biR->max_comps = size; /* give some space to spare */ + } + + biR->size = size; + biR->refs = 1; + biR->next = NULL; + ctx->active_count++; + return biR; +} + +/* + * Work out the highest '1' bit in an exponent. Used when doing sliding-window + * exponentiation. + */ +static int find_max_exp_index(bigint *biexp) +{ + int i = COMP_BIT_SIZE-1; + comp shift = COMP_RADIX/2; + comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */ + + check(biexp); + + do + { + if (test & shift) + { + return i+(biexp->size-1)*COMP_BIT_SIZE; + } + + shift >>= 1; + } while (--i != 0); + + return -1; /* error - must have been a leading 0 */ +} + +/* + * Is a particular bit is an exponent 1 or 0? Used when doing sliding-window + * exponentiation. + */ +static int exp_bit_is_one(bigint *biexp, int offset) +{ + comp test = biexp->comps[offset / COMP_BIT_SIZE]; + int num_shifts = offset % COMP_BIT_SIZE; + comp shift = 1; + int i; + + check(biexp); + + for (i = 0; i < num_shifts; i++) + { + shift <<= 1; + } + + return test & shift; +} + +#ifdef CONFIG_BIGINT_CHECK_ON +/* + * Perform a sanity check on bi. + */ +static void check(const bigint *bi) +{ + if (bi->refs <= 0) + { + printf("check: zero or negative refs in bigint\n"); + abort(); + } + + if (bi->next != NULL) + { + printf("check: attempt to use a bigint from " + "the free list\n"); + abort(); + } +} +#endif + +/* + * Delete any leading 0's (and allow for 0). + */ +static bigint *trim(bigint *bi) +{ + check(bi); + + while (bi->comps[bi->size-1] == 0 && bi->size > 1) + { + bi->size--; + } + + return bi; +} + +#if defined(CONFIG_BIGINT_MONTGOMERY) +/** + * @brief Perform a single montgomery reduction. + * @param ctx [in] The bigint session context. + * @param bixy [in] A bigint. + * @return The result of the montgomery reduction. + */ +bigint *bi_mont(BI_CTX *ctx, bigint *bixy) +{ + int i = 0, n; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + comp mod_inv = ctx->N0_dash[mod_offset]; + + check(bixy); + + if (ctx->use_classical) /* just use classical instead */ + { + return bi_mod(ctx, bixy); + } + + n = bim->size; + + do + { + bixy = bi_add(ctx, bixy, comp_left_shift( + bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i)); + } while (++i < n); + + comp_right_shift(bixy, n); + + if (bi_compare(bixy, bim) >= 0) + { + bixy = bi_subtract(ctx, bixy, bim, NULL); + } + + return bixy; +} + +#elif defined(CONFIG_BIGINT_BARRETT) +/* + * Stomp on the most significant components to give the illusion of a "mod base + * radix" operation + */ +static bigint *comp_mod(bigint *bi, int mod) +{ + check(bi); + + if (bi->size > mod) + { + bi->size = mod; + } + + return bi; +} + +/* + * Barrett reduction has no need for some parts of the product, so ignore bits + * of the multiply. This routine gives Barrett its big performance + * improvements over Classical/Montgomery reduction methods. + */ +static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, + int inner_partial, int outer_partial) +{ + int i = 0, j, n = bia->size, t = bib->size; + bigint *biR; + comp carry; + comp *sr, *sa, *sb; + + check(bia); + check(bib); + + biR = alloc(ctx, n + t); + sa = bia->comps; + sb = bib->comps; + sr = biR->comps; + + if (inner_partial) + { + memset(sr, 0, inner_partial*COMP_BYTE_SIZE); + } + else /* outer partial */ + { + if (n < outer_partial || t < outer_partial) /* should we bother? */ + { + bi_free(ctx, bia); + bi_free(ctx, bib); + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + memset(&sr[outer_partial], 0, (n+t-outer_partial)*COMP_BYTE_SIZE); + } + + do + { + comp *a = sa; + comp b = *sb++; + long_comp tmp; + int i_plus_j = i; + carry = 0; + j = n; + + if (outer_partial && i_plus_j < outer_partial) + { + i_plus_j = outer_partial; + a = &sa[outer_partial-i]; + j = n-(outer_partial-i); + } + + do + { + if (inner_partial && i_plus_j >= inner_partial) + { + break; + } + + tmp = sr[i_plus_j] + ((long_comp)*a++)*b + carry; + sr[i_plus_j++] = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (--j != 0); + + sr[i_plus_j] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +/** + * @brief Perform a single Barrett reduction. + * @param ctx [in] The bigint session context. + * @param bi [in] A bigint. + * @return The result of the Barrett reduction. + */ +bigint *bi_barrett(BI_CTX *ctx, bigint *bi) +{ + bigint *q1, *q2, *q3, *r1, *r2, *r; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + int k = bim->size; + + check(bi); + check(bim); + + /* use Classical method instead - Barrett cannot help here */ + if (bi->size > k*2) + { + return bi_mod(ctx, bi); + } + + q1 = comp_right_shift(bi_clone(ctx, bi), k-1); + + /* do outer partial multiply */ + q2 = partial_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1); + q3 = comp_right_shift(q2, k+1); + r1 = comp_mod(bi, k+1); + + /* do inner partial multiply */ + r2 = comp_mod(partial_multiply(ctx, q3, bim, k+1, 0), k+1); + r = bi_subtract(ctx, r1, r2, NULL); + + /* if (r >= m) r = r - m; */ + if (bi_compare(r, bim) >= 0) + { + r = bi_subtract(ctx, r, bim, NULL); + } + + return r; +} +#endif /* CONFIG_BIGINT_BARRETT */ + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW +/* + * Work out g1, g3, g5, g7... etc for the sliding-window algorithm + */ +static void precompute_slide_window(BI_CTX *ctx, int window, bigint *g1) +{ + int k = 1, i; + bigint *g2; + + for (i = 0; i < window-1; i++) /* compute 2^(window-1) */ + { + k <<= 1; + } + + ctx->g = (bigint **)malloc(k*sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, g1); + bi_permanent(ctx->g[0]); + g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */ + + for (i = 1; i < k; i++) + { + ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2))); + bi_permanent(ctx->g[i]); + } + + bi_free(ctx, g2); + ctx->window = k; +} +#endif + +/** + * @brief Perform a modular exponentiation. + * + * This function requires bi_set_mod() to have been called previously. This is + * one of the optimisations used for performance. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint on which to perform the mod power operation. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) +{ + int i = find_max_exp_index(biexp), j, window_size = 1; + bigint *biR = int_to_bi(ctx, 1); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + uint8_t mod_offset = ctx->mod_offset; + if (!ctx->use_classical) + { + /* preconvert */ + bi = bi_mont(ctx, + bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */ + bi_free(ctx, biR); + biR = ctx->bi_R_mod_m[mod_offset]; /* A */ + } +#endif + + check(bi); + check(biexp); + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW + for (j = i; j > 32; j /= 5) /* work out an optimum size */ + window_size++; + + /* work out the slide constants */ + precompute_slide_window(ctx, window_size, bi); +#else /* just one constant */ + ctx->g = (bigint **)malloc(sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, bi); + ctx->window = 1; + bi_permanent(ctx->g[0]); +#endif + + /* if sliding-window is off, then only one bit will be done at a time and + * will reduce to standard left-to-right exponentiation */ + do + { + if (exp_bit_is_one(biexp, i)) + { + int l = i-window_size+1; + int part_exp = 0; + + if (l < 0) /* LSB of exponent will always be 1 */ + l = 0; + else + { + while (exp_bit_is_one(biexp, l) == 0) + l++; /* go back up */ + } + + /* build up the section of the exponent */ + for (j = i; j >= l; j--) + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + if (exp_bit_is_one(biexp, j)) + part_exp++; + + if (j != l) + part_exp <<= 1; + } + + part_exp = (part_exp-1)/2; /* adjust for array */ + biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp])); + i = l-1; + } + else /* square it */ + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + i--; + } + } while (i >= 0); + + /* cleanup */ + for (i = 0; i < ctx->window; i++) + { + bi_depermanent(ctx->g[i]); + bi_free(ctx, ctx->g[i]); + } + + free(ctx->g); + bi_free(ctx, bi); + bi_free(ctx, biexp); +#if defined CONFIG_BIGINT_MONTGOMERY + return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */ +#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */ + return biR; +#endif +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * @brief Perform a modular exponentiation using a temporary modulus. + * + * We need this function to check the signatures of certificates. The modulus + * of this function is temporary as it's just used for authentication. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param bim [in] The temporary modulus. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp) +{ + bigint *biR, *tmp_biR; + + /* Set up a temporary bigint context and transfer what we need between + * them. We need to do this since we want to keep the original modulus + * which is already in this context. This operation is only called when + * doing peer verification, and so is not expensive :-) */ + BI_CTX *tmp_ctx = bi_initialize(); + bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET); + tmp_biR = bi_mod_power(tmp_ctx, + bi_clone(tmp_ctx, bi), + bi_clone(tmp_ctx, biexp)); + biR = bi_clone(ctx, tmp_biR); + bi_free(tmp_ctx, tmp_biR); + bi_free_mod(tmp_ctx, BIGINT_M_OFFSET); + bi_terminate(tmp_ctx); + + bi_free(ctx, bi); + bi_free(ctx, bim); + bi_free(ctx, biexp); + return biR; +} +#endif + +#ifdef CONFIG_BIGINT_CRT +/** + * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. + * + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param dP [in] CRT's dP bigint + * @param dQ [in] CRT's dQ bigint + * @param p [in] CRT's p bigint + * @param q [in] CRT's q bigint + * @param qInv [in] CRT's qInv bigint + * @return The result of the CRT operation + */ +bigint *bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, bigint *qInv) +{ + bigint *m1, *m2, *h; + + /* Montgomery has a condition the 0 < x, y < m and these products violate + * that condition. So disable Montgomery when using CRT */ +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 1; +#endif + ctx->mod_offset = BIGINT_P_OFFSET; + m1 = bi_mod_power(ctx, bi_copy(bi), dP); + + ctx->mod_offset = BIGINT_Q_OFFSET; + m2 = bi_mod_power(ctx, bi, dQ); + + h = bi_subtract(ctx, bi_add(ctx, m1, p), bi_copy(m2), NULL); + h = bi_multiply(ctx, h, qInv); + ctx->mod_offset = BIGINT_P_OFFSET; + h = bi_residue(ctx, h); +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 0; /* reset for any further operation */ +#endif + return bi_add(ctx, m2, bi_multiply(ctx, q, h)); +} +#endif +/** @} */ diff --git a/libs/nixio/axTLS/crypto/bigint.h b/libs/nixio/axTLS/crypto/bigint.h new file mode 100644 index 000000000..2966a3edb --- /dev/null +++ b/libs/nixio/axTLS/crypto/bigint.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#ifndef BIGINT_HEADER +#define BIGINT_HEADER + +#include "crypto.h" + +BI_CTX *bi_initialize(void); +void bi_terminate(BI_CTX *ctx); +void bi_permanent(bigint *bi); +void bi_depermanent(bigint *bi); +void bi_clear_cache(BI_CTX *ctx); +void bi_free(BI_CTX *ctx, bigint *bi); +bigint *bi_copy(bigint *bi); +bigint *bi_clone(BI_CTX *ctx, const bigint *bi); +void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); +bigint *int_to_bi(BI_CTX *ctx, comp i); + +/* the functions that actually do something interesting */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_subtract(BI_CTX *ctx, bigint *bia, + bigint *bib, int *is_negative); +bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); +int bi_compare(bigint *bia, bigint *bib); +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); +void bi_free_mod(BI_CTX *ctx, int mod_offset); + +#ifdef CONFIG_SSL_FULL_MODE +void bi_print(const char *label, bigint *bi); +bigint *bi_str_import(BI_CTX *ctx, const char *data); +#endif + +/** + * @def bi_mod + * Find the residue of B. bi_set_mod() must be called before hand. + */ +#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) + +/** + * bi_residue() is technically the same as bi_mod(), but it uses the + * appropriate reduction technique (which is bi_mod() when doing classical + * reduction). + */ +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B) bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#elif defined(CONFIG_BIGINT_BARRETT) +#define bi_residue(A, B) bi_barrett(A, B) +bigint *bi_barrett(BI_CTX *ctx, bigint *bi); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B) bi_mod(A, B) +#endif + +#ifdef CONFIG_BIGINT_SQUARE +bigint *bi_square(BI_CTX *ctx, bigint *bi); +#else +#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) +#endif + +#ifdef CONFIG_BIGINT_CRT +bigint *bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, + bigint *qInv); +#endif + +#endif diff --git a/libs/nixio/axTLS/crypto/bigint_impl.h b/libs/nixio/axTLS/crypto/bigint_impl.h new file mode 100644 index 000000000..1483154a4 --- /dev/null +++ b/libs/nixio/axTLS/crypto/bigint_impl.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#ifndef BIGINT_IMPL_HEADER +#define BIGINT_IMPL_HEADER + +/* Maintain a number of precomputed variables when doing reduction */ +#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ +#ifdef CONFIG_BIGINT_CRT +#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ +#define BIGINT_Q_OFFSET 2 /**< q module offset. */ +#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ +#else +#define BIGINT_NUM_MODS 1 +#endif + +/* Architecture specific functions for big ints */ +#ifdef WIN32 +#define COMP_RADIX 4294967296i64 +#define COMP_MAX 0xFFFFFFFFFFFFFFFFui64 +#else +#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ +#define COMP_MAX 0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ +#endif +#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ + +typedef uint32_t comp; /**< A single precision component. */ +typedef uint64_t long_comp; /**< A double precision component. */ +typedef int64_t slong_comp; /**< A signed double precision component. */ + +/** + * @struct _bigint + * @brief A big integer basic object + */ +struct _bigint +{ + struct _bigint* next; /**< The next bigint in the cache. */ + short size; /**< The number of components in this bigint. */ + short max_comps; /**< The heapsize allocated for this bigint */ + int refs; /**< An internal reference count. */ + comp* comps; /**< A ptr to the actual component data */ +}; + +typedef struct _bigint bigint; /**< An alias for _bigint */ + +/** + * Maintains the state of the cache, and a number of variables used in + * reduction. + */ +typedef struct /**< A big integer "session" context. */ +{ + bigint *active_list; /**< Bigints currently used. */ + bigint *free_list; /**< Bigints not used. */ + bigint *bi_radix; /**< The radix used. */ + bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ + +#if defined(CONFIG_BIGINT_MONTGOMERY) + bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ + bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ + comp N0_dash[BIGINT_NUM_MODS]; +#elif defined(CONFIG_BIGINT_BARRETT) + bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ +#endif + bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ + bigint **g; /**< Used by sliding-window. */ + int window; /**< The size of the sliding window */ + int active_count; /**< Number of active bigints. */ + int free_count; /**< Number of free bigints. */ + +#ifdef CONFIG_BIGINT_MONTGOMERY + uint8_t use_classical; /**< Use classical reduction. */ +#endif + uint8_t mod_offset; /**< The mod offset we are using */ +} BI_CTX; + +#ifndef WIN32 +#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ +#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ +#endif + +#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ + +#endif diff --git a/libs/nixio/axTLS/crypto/crypto.h b/libs/nixio/axTLS/crypto/crypto.h new file mode 100644 index 000000000..5c95f2159 --- /dev/null +++ b/libs/nixio/axTLS/crypto/crypto.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @file crypto.h + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" +#include "os_port.h" +#include "bigint_impl.h" +#include "bigint.h" + +/* enable features based on a 'super-set' capbaility. */ +#if defined(CONFIG_SSL_FULL_MODE) +#define CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_CERT_VERIFICATION +#elif defined(CONFIG_SSL_ENABLE_CLIENT) +#define CONFIG_SSL_CERT_VERIFICATION +#endif + +/************************************************************************** + * AES declarations + **************************************************************************/ + +#define AES_MAXROUNDS 14 +#define AES_BLOCKSIZE 16 +#define AES_IV_SIZE 16 + +typedef struct aes_key_st +{ + uint16_t rounds; + uint16_t key_size; + uint32_t ks[(AES_MAXROUNDS+1)*8]; + uint8_t iv[AES_IV_SIZE]; +} AES_CTX; + +typedef enum +{ + AES_MODE_128, + AES_MODE_256 +} AES_MODE; + +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode); +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, + uint8_t *out, int length); +void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +void AES_convert_key(AES_CTX *ctx); + +/************************************************************************** + * RC4 declarations + **************************************************************************/ + +typedef struct +{ + uint8_t x, y, m[256]; +} RC4_CTX; + +void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); +void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); + +/************************************************************************** + * SHA1 declarations + **************************************************************************/ + +#define SHA1_SIZE 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct +{ + uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + uint16_t Message_Block_Index; /* Index into message block array */ + uint8_t Message_Block[64]; /* 512-bit message blocks */ +} SHA1_CTX; + +void SHA1_Init(SHA1_CTX *); +void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); +void SHA1_Final(uint8_t *digest, SHA1_CTX *); + +/************************************************************************** + * MD2 declarations + **************************************************************************/ + +#define MD2_SIZE 16 + +typedef struct +{ + unsigned char cksum[16]; /* checksum of the data block */ + unsigned char state[48]; /* intermediate digest state */ + unsigned char buffer[16]; /* data block being processed */ + int left; /* amount of data in buffer */ +} MD2_CTX; + +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); +EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); + +/************************************************************************** + * MD5 declarations + **************************************************************************/ + +#define MD5_SIZE 16 + +typedef struct +{ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + uint8_t buffer[64]; /* input buffer */ +} MD5_CTX; + +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); + +/************************************************************************** + * HMAC declarations + **************************************************************************/ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +/************************************************************************** + * RSA declarations + **************************************************************************/ + +typedef struct +{ + bigint *m; /* modulus */ + bigint *e; /* public exponent */ + bigint *d; /* private exponent */ +#ifdef CONFIG_BIGINT_CRT + bigint *p; /* p as in m = pq */ + bigint *q; /* q as in m = pq */ + bigint *dP; /* d mod (p-1) */ + bigint *dQ; /* d mod (q-1) */ + bigint *qInv; /* q^-1 mod p */ +#endif + int num_octets; + BI_CTX *bi_ctx; +} RSA_CTX; + +void RSA_priv_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ); +void RSA_pub_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len); +void RSA_free(RSA_CTX *ctx); +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, + int is_decryption); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp); +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing); +void RSA_print(const RSA_CTX *ctx); +#endif + +/************************************************************************** + * RNG declarations + **************************************************************************/ +EXP_FUNC void STDCALL RNG_initialize(const uint8_t *seed_buf, int size); +EXP_FUNC void STDCALL RNG_terminate(void); +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/nixio/axTLS/crypto/crypto_misc.c b/libs/nixio/axTLS/crypto/crypto_misc.c new file mode 100644 index 000000000..59b72ec08 --- /dev/null +++ b/libs/nixio/axTLS/crypto/crypto_misc.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Some misc. routines to help things out + */ + +#include +#include +#include +#include +#include "crypto_misc.h" +#ifdef CONFIG_WIN32_USE_CRYPTO_LIB +#include "wincrypt.h" +#endif + +#ifndef WIN32 +static int rng_fd = -1; +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) +static HCRYPTPROV gCryptProv; +#endif + +#if (!defined(CONFIG_USE_DEV_URANDOM) && !defined(CONFIG_WIN32_USE_CRYPTO_LIB)) +static uint64_t rng_num; +#endif + +static int rng_ref_count; +const char * const unsupported_str = "Error: Feature not supported\n"; + +#ifndef CONFIG_SSL_SKELETON_MODE +/** + * Retrieve a file and put it into memory + * @return The size of the file, or -1 on failure. + */ +int get_file(const char *filename, uint8_t **buf) +{ + int total_bytes = 0; + int bytes_read = 0; + int filesize; + FILE *stream = fopen(filename, "rb"); + + if (stream == NULL) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("file '%s' does not exist\n", filename); TTY_FLUSH(); +#endif + return -1; + } + + /* Win CE doesn't support stat() */ + fseek(stream, 0, SEEK_END); + filesize = ftell(stream); + *buf = (uint8_t *)malloc(filesize); + fseek(stream, 0, SEEK_SET); + + do + { + bytes_read = fread(*buf+total_bytes, 1, filesize-total_bytes, stream); + total_bytes += bytes_read; + } while (total_bytes < filesize && bytes_read > 0); + + fclose(stream); + return filesize; +} +#endif + +/** + * Initialise the Random Number Generator engine. + * - On Win32 use the platform SDK's crypto engine. + * - On Linux use /dev/urandom + * - If none of these work then use a custom RNG. + */ +EXP_FUNC void STDCALL RNG_initialize(const uint8_t *seed_buf, int size) +{ + if (rng_ref_count == 0) + { +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + rng_fd = ax_open("/dev/urandom", O_RDONLY); +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + if (!CryptAcquireContext(&gCryptProv, + NULL, NULL, PROV_RSA_FULL, 0)) + { + if (GetLastError() == NTE_BAD_KEYSET && + !CryptAcquireContext(&gCryptProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_NEWKEYSET)) + { + printf("CryptoLib: %x\n", unsupported_str, GetLastError()); + exit(1); + } + } +#else + /* help seed with the user's private key - this is a number that + should be hard to find, due to the fact that it relies on knowing + the private key */ + int i; + + for (i = 0; i < size/(int)sizeof(uint64_t); i++) + rng_num ^= *((uint64_t *)&seed_buf[i*sizeof(uint64_t)]); + + srand((long)&seed_buf); /* use the stack ptr as another rnd seed */ +#endif + } + + rng_ref_count++; +} + +/** + * Terminate the RNG engine. + */ +EXP_FUNC void STDCALL RNG_terminate(void) +{ + if (--rng_ref_count == 0) + { +#ifndef WIN32 + close(rng_fd); +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) + CryptReleaseContext(gCryptProv, 0); +#endif + } +} + +/** + * Set a series of bytes with a random number. Individual bytes can be 0 + */ +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data) +{ +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + /* use the Linux default */ + read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */ +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + /* use Microsoft Crypto Libraries */ + CryptGenRandom(gCryptProv, num_rand_bytes, rand_data); +#else /* nothing else to use, so use a custom RNG */ + /* The method we use when we've got nothing better. Use RC4, time + and a couple of random seeds to generate a random sequence */ + RC4_CTX rng_ctx; + struct timeval tv; + uint64_t big_num1, big_num2; + + gettimeofday(&tv, NULL); /* yes I know we shouldn't do this */ + + /* all numbers by themselves are pretty simple, but combined should + * be a challenge */ + big_num1 = (uint64_t)tv.tv_sec*(tv.tv_usec+1); + big_num2 = (uint64_t)rand()*big_num1; + big_num1 ^= rng_num; + + memcpy(rand_data, &big_num1, sizeof(uint64_t)); + if (num_rand_bytes > sizeof(uint64_t)) + memcpy(&rand_data[8], &big_num2, sizeof(uint64_t)); + + if (num_rand_bytes > 16) + { + /* clear rest of data */ + memset(&rand_data[16], 0, num_rand_bytes-16); + } + + RC4_setup(&rng_ctx, rand_data, 16); /* use as a key */ + RC4_crypt(&rng_ctx, rand_data, rand_data, num_rand_bytes); + + /* use last 8 bytes for next time */ + memcpy(&rng_num, &rand_data[num_rand_bytes-8], sizeof(uint64_t)); +#endif +} + +/** + * Set a series of bytes with a random number. Individual bytes are not zero. + */ +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) +{ + int i; + get_random(num_rand_bytes, rand_data); + + for (i = 0; i < num_rand_bytes; i++) + { + while (rand_data[i] == 0) /* can't be 0 */ + rand_data[i] = (uint8_t)(rand()); + } +} + +/** + * Some useful diagnostic routines + */ +#if defined(CONFIG_SSL_FULL_MODE) || defined(CONFIG_DEBUG) +int hex_finish; +int hex_index; + +static void print_hex_init(int finish) +{ + hex_finish = finish; + hex_index = 0; +} + +static void print_hex(uint8_t hex) +{ + static int column; + + if (hex_index == 0) + { + column = 0; + } + + printf("%02x ", hex); + if (++column == 8) + { + printf(": "); + } + else if (column >= 16) + { + printf("\n"); + column = 0; + } + + if (++hex_index >= hex_finish && column > 0) + { + printf("\n"); + } +} + +/** + * Spit out a blob of data for diagnostics. The data is is a nice column format + * for easy reading. + * + * @param format [in] The string (with possible embedded format characters) + * @param size [in] The number of numbers to print + * @param data [in] The start of data to use + * @param ... [in] Any additional arguments + */ +EXP_FUNC void STDCALL print_blob(const char *format, + const uint8_t *data, int size, ...) +{ + int i; + char tmp[80]; + va_list(ap); + + va_start(ap, size); + sprintf(tmp, "%s\n", format); + vprintf(tmp, ap); + print_hex_init(size); + for (i = 0; i < size; i++) + { + print_hex(data[i]); + } + + va_end(ap); + TTY_FLUSH(); +} +#elif defined(WIN32) +/* VC6.0 doesn't handle variadic macros */ +EXP_FUNC void STDCALL print_blob(const char *format, const unsigned char *data, + int size, ...) {} +#endif + +#if defined(CONFIG_SSL_HAS_PEM) || defined(CONFIG_HTTP_HAS_AUTHORIZATION) +/* base64 to binary lookup table */ +static const uint8_t map[128] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 254, 255, 255, 255, 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, 255, 255, 255, 255, 255, + 255, 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, 255, 255, 255, 255, 255 +}; + +EXP_FUNC int STDCALL base64_decode(const char *in, int len, + uint8_t *out, int *outlen) +{ + int g, t, x, y, z; + uint8_t c; + int ret = -1; + + g = 3; + for (x = y = z = t = 0; x < len; x++) + { + if ((c = map[in[x]&0x7F]) == 0xff) + continue; + + if (c == 254) /* this is the end... */ + { + c = 0; + + if (--g < 0) + goto error; + } + else if (g != 3) /* only allow = at end */ + goto error; + + t = (t<<6) | c; + + if (++y == 4) + { + out[z++] = (uint8_t)((t>>16)&255); + + if (g > 1) + out[z++] = (uint8_t)((t>>8)&255); + + if (g > 2) + out[z++] = (uint8_t)(t&255); + + y = t = 0; + } + } + + if (y != 0) + goto error; + + if (outlen) + *outlen = z; + ret = 0; + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret < 0) + printf("Error: Invalid base64\n"); TTY_FLUSH(); +#endif + TTY_FLUSH(); + return ret; + +} +#endif + diff --git a/libs/nixio/axTLS/crypto/hmac.c b/libs/nixio/axTLS/crypto/hmac.c new file mode 100644 index 000000000..9199ff220 --- /dev/null +++ b/libs/nixio/axTLS/crypto/hmac.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * HMAC implementation - This code was originally taken from RFC2104 + */ + +#include +#include "crypto.h" + +/** + * Perform HMAC-MD5 + */ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + MD5_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5_Init(&context); + MD5_Update(&context, k_ipad, 64); + MD5_Update(&context, msg, length); + MD5_Final(digest, &context); + MD5_Init(&context); + MD5_Update(&context, k_opad, 64); + MD5_Update(&context, digest, MD5_SIZE); + MD5_Final(digest, &context); +} + +/** + * Perform HMAC-SHA1 + */ +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + SHA1_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + SHA1_Init(&context); + SHA1_Update(&context, k_ipad, 64); + SHA1_Update(&context, msg, length); + SHA1_Final(digest, &context); + SHA1_Init(&context); + SHA1_Update(&context, k_opad, 64); + SHA1_Update(&context, digest, SHA1_SIZE); + SHA1_Final(digest, &context); +} diff --git a/libs/nixio/axTLS/crypto/md2.c b/libs/nixio/axTLS/crypto/md2.c new file mode 100644 index 000000000..bfcbd24bb --- /dev/null +++ b/libs/nixio/axTLS/crypto/md2.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * RFC 1115/1319 compliant MD2 implementation + * The MD2 algorithm was designed by Ron Rivest in 1989. + * + * http://www.ietf.org/rfc/rfc1115.txt + * http://www.ietf.org/rfc/rfc1319.txt + */ + +#include +#include + +#include "crypto.h" + +/** + * This code is only here to enable the verification of Verisign root + * certificates. So only enable it for verification mode. + */ +#ifdef CONFIG_SSL_CERT_VERIFICATION + +static const uint8_t PI_SUBST[256] = +{ + 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, + 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 0x62, 0xA7, 0x05, 0xF3, + 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, + 0x82, 0xCA, 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, + 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 0xBE, 0x4E, + 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, + 0xBB, 0x2F, 0xEE, 0x7A, 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, + 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, + 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, + 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 0xFF, 0x19, 0x30, 0xB3, + 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, + 0xAA, 0xC6, 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, + 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 0x45, 0x9D, + 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, + 0xE6, 0x2D, 0xA8, 0x02, 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, + 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, + 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, + 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 0x2C, 0x53, 0x0D, 0x6E, + 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, + 0x4D, 0x52, 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, + 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 0x78, 0x88, + 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, + 0x3B, 0x00, 0x1D, 0x39, 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, + 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, + 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, + 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 +}; + +/* + * MD2 context setup + */ +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx) +{ + memset(ctx, 0, sizeof *ctx); +} + +static void md2_process(MD2_CTX *ctx) +{ + int i, j; + uint8_t t = 0; + + for (i = 0; i < 16; i++) + { + ctx->state[i + 16] = ctx->buffer[i]; + ctx->state[i + 32] = ctx->buffer[i] ^ ctx->state[i]; + } + + for (i = 0; i < 18; i++) + { + for (j = 0; j < 48; j++) + t = (ctx->state[j] ^= PI_SUBST[t]); + + t = (t + i) & 0xFF; + } + + t = ctx->cksum[15]; + + for (i = 0; i < 16; i++) + t = (ctx->cksum[i] ^= PI_SUBST[ctx->buffer[i] ^ t]); +} + +/* + * MD2 process buffer + */ +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen) +{ + int fill; + + while (ilen > 0) + { + if (ctx->left + ilen > 16) + fill = 16 - ctx->left; + else + fill = ilen; + + memcpy(ctx->buffer + ctx->left, input, fill); + + ctx->left += fill; + input += fill; + ilen -= fill; + + if (ctx->left == 16) + { + ctx->left = 0; + md2_process(ctx); + } + } +} + +/* + * MD2 final digest + */ +EXP_FUNC void STDCALL MD2_Final(uint8_t *output, MD2_CTX *ctx) +{ + int i; + uint8_t x; + + x = (uint8_t)(16 - ctx->left); + + for (i = ctx->left; i < 16; i++) + ctx->buffer[i] = x; + + md2_process(ctx); + + memcpy(ctx->buffer, ctx->cksum, 16); + md2_process(ctx); + + memcpy(output, ctx->state, 16); +} + +#endif diff --git a/libs/nixio/axTLS/crypto/md5.c b/libs/nixio/axTLS/crypto/md5.c new file mode 100644 index 000000000..b4f86cab5 --- /dev/null +++ b/libs/nixio/axTLS/crypto/md5.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * This file implements the MD5 algorithm as defined in RFC1321 + */ + +#include +#include "crypto.h" + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/* ----- static functions ----- */ +static void MD5Transform(uint32_t state[4], const uint8_t block[64]); +static void Encode(uint8_t *output, uint32_t *input, uint32_t len); +static void Decode(uint32_t *output, const uint8_t *input, uint32_t len); + +static const uint8_t PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/** + * MD5 initialization - begins an MD5 operation, writing a new ctx. + */ +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *ctx) +{ + ctx->count[0] = ctx->count[1] = 0; + + /* Load magic initialization constants. + */ + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *ctx, const uint8_t * msg, int len) +{ + uint32_t x; + int i, partLen; + + /* Compute number of bytes mod 64 */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((ctx->count[0] += ((uint32_t)len << 3)) < ((uint32_t)len << 3)) + ctx->count[1]++; + ctx->count[1] += ((uint32_t)len >> 29); + + partLen = 64 - x; + + /* Transform as many times as possible. */ + if (len >= partLen) + { + memcpy(&ctx->buffer[x], msg, partLen); + MD5Transform(ctx->state, ctx->buffer); + + for (i = partLen; i + 63 < len; i += 64) + MD5Transform(ctx->state, &msg[i]); + + x = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&ctx->buffer[x], &msg[i], len-i); +} + +/** + * Return the 128-bit message digest into the user's array + */ +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *ctx) +{ + uint8_t bits[8]; + uint32_t x, padLen; + + /* Save number of bits */ + Encode(bits, ctx->count, 8); + + /* Pad out to 56 mod 64. + */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3f); + padLen = (x < 56) ? (56 - x) : (120 - x); + MD5_Update(ctx, PADDING, padLen); + + /* Append length (before padding) */ + MD5_Update(ctx, bits, 8); + + /* Store state in digest */ + Encode(digest, ctx->state, MD5_SIZE); +} + +/** + * MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform(uint32_t state[4], const uint8_t block[64]) +{ + uint32_t a = state[0], b = state[1], c = state[2], + d = state[3], x[MD5_SIZE]; + + Decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/** + * Encodes input (uint32_t) into output (uint8_t). Assumes len is + * a multiple of 4. + */ +static void Encode(uint8_t *output, uint32_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (uint8_t)(input[i] & 0xff); + output[j+1] = (uint8_t)((input[i] >> 8) & 0xff); + output[j+2] = (uint8_t)((input[i] >> 16) & 0xff); + output[j+3] = (uint8_t)((input[i] >> 24) & 0xff); + } +} + +/** + * Decodes input (uint8_t) into output (uint32_t). Assumes len is + * a multiple of 4. + */ +static void Decode(uint32_t *output, const uint8_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); +} diff --git a/libs/nixio/axTLS/crypto/rc4.c b/libs/nixio/axTLS/crypto/rc4.c new file mode 100644 index 000000000..57136b82b --- /dev/null +++ b/libs/nixio/axTLS/crypto/rc4.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * An implementation of the RC4/ARC4 algorithm. + * Originally written by Christophe Devine. + */ + +#include +#include "crypto.h" + +/** + * Get ready for an encrypt/decrypt operation + */ +void RC4_setup(RC4_CTX *ctx, const uint8_t *key, int length) +{ + int i, j = 0, k = 0, a; + uint8_t *m; + + ctx->x = 0; + ctx->y = 0; + m = ctx->m; + + for (i = 0; i < 256; i++) + m[i] = i; + + for (i = 0; i < 256; i++) + { + a = m[i]; + j = (uint8_t)(j + a + key[k]); + m[i] = m[j]; + m[j] = a; + + if (++k >= length) + k = 0; + } +} + +/** + * Perform the encrypt/decrypt operation (can use it for either since + * this is a stream cipher). + */ +void RC4_crypt(RC4_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint8_t *m, x, y, a, b; + out = (uint8_t *)msg; + + x = ctx->x; + y = ctx->y; + m = ctx->m; + + for (i = 0; i < length; i++) + { + a = m[++x]; + y += a; + m[x] = b = m[y]; + m[y] = a; + out[i] ^= m[(uint8_t)(a + b)]; + } + + ctx->x = x; + ctx->y = y; +} diff --git a/libs/nixio/axTLS/crypto/rsa.c b/libs/nixio/axTLS/crypto/rsa.c new file mode 100644 index 000000000..31627bb31 --- /dev/null +++ b/libs/nixio/axTLS/crypto/rsa.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Implements the RSA public encryption algorithm. Uses the bigint library to + * perform its calculations. + */ + +#include +#include +#include +#include +#include "crypto.h" + +void RSA_priv_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#if CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len); + rsa_ctx = *ctx; + bi_ctx = rsa_ctx->bi_ctx; + rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len); + bi_permanent(rsa_ctx->d); + +#ifdef CONFIG_BIGINT_CRT + rsa_ctx->p = bi_import(bi_ctx, p, p_len); + rsa_ctx->q = bi_import(bi_ctx, q, q_len); + rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len); + rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len); + rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len); + bi_permanent(rsa_ctx->dP); + bi_permanent(rsa_ctx->dQ); + bi_permanent(rsa_ctx->qInv); + bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET); + bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET); +#endif +} + +void RSA_pub_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + + if (*ctx) /* if we load multiple certs, dump the old one */ + RSA_free(*ctx); + + bi_ctx = bi_initialize(); + *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); + rsa_ctx = *ctx; + rsa_ctx->bi_ctx = bi_ctx; + rsa_ctx->num_octets = (mod_len & 0xFFF0); + rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len); + bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET); + rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len); + bi_permanent(rsa_ctx->e); +} + +/** + * Free up any RSA context resources. + */ +void RSA_free(RSA_CTX *rsa_ctx) +{ + BI_CTX *bi_ctx; + if (rsa_ctx == NULL) /* deal with ptrs that are null */ + return; + + bi_ctx = rsa_ctx->bi_ctx; + + bi_depermanent(rsa_ctx->e); + bi_free(bi_ctx, rsa_ctx->e); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET); + + if (rsa_ctx->d) + { + bi_depermanent(rsa_ctx->d); + bi_free(bi_ctx, rsa_ctx->d); +#ifdef CONFIG_BIGINT_CRT + bi_depermanent(rsa_ctx->dP); + bi_depermanent(rsa_ctx->dQ); + bi_depermanent(rsa_ctx->qInv); + bi_free(bi_ctx, rsa_ctx->dP); + bi_free(bi_ctx, rsa_ctx->dQ); + bi_free(bi_ctx, rsa_ctx->qInv); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET); +#endif + } + + bi_terminate(bi_ctx); + free(rsa_ctx); +} + +/** + * @brief Use PKCS1.5 for decryption/verification. + * @param ctx [in] The context + * @param in_data [in] The data to encrypt (must be < modulus size-11) + * @param out_data [out] The encrypted data. + * @param is_decryption [in] Decryption or verify operation. + * @return The number of bytes that were originally encrypted. -1 on error. + * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, + uint8_t *out_data, int is_decryption) +{ + const int byte_size = ctx->num_octets; + int i, size; + bigint *decrypted_bi, *dat_bi; + uint8_t *block = (uint8_t *)alloca(byte_size); + + memset(out_data, 0, byte_size); /* initialise */ + + /* decrypt */ + dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size); +#ifdef CONFIG_SSL_CERT_VERIFICATION + decrypted_bi = is_decryption ? /* decrypt or verify? */ + RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi); +#else /* always a decryption */ + decrypted_bi = RSA_private(ctx, dat_bi); +#endif + + /* convert to a normal block */ + bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size); + + i = 10; /* start at the first possible non-padded byte */ + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */ + { + while (block[i++] == 0xff && i < byte_size); + + if (block[i-2] != 0xff) + i = byte_size; /*ensure size is 0 */ + } + else /* PKCS1.5 encryption padding is random */ +#endif + { + while (block[i++] && i < byte_size); + } + size = byte_size - i; + + /* get only the bit we want */ + if (size > 0) + memcpy(out_data, &block[i], size); + + return size ? size : -1; +} + +/** + * Performs m = c^d mod n + */ +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg) +{ +#ifdef CONFIG_BIGINT_CRT + return bi_crt(c->bi_ctx, bi_msg, c->dP, c->dQ, c->p, c->q, c->qInv); +#else + BI_CTX *ctx = c->bi_ctx; + ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(ctx, bi_msg, c->d); +#endif +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * Used for diagnostics. + */ +void RSA_print(const RSA_CTX *rsa_ctx) +{ + if (rsa_ctx == NULL) + return; + + printf("----------------- RSA DEBUG ----------------\n"); + printf("Size:\t%d\n", rsa_ctx->num_octets); + bi_print("Modulus", rsa_ctx->m); + bi_print("Public Key", rsa_ctx->e); + bi_print("Private Key", rsa_ctx->d); +} +#endif + +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +/** + * Performs c = m^e mod n + */ +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg) +{ + c->bi_ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(c->bi_ctx, bi_msg, c->e); +} + +/** + * Use PKCS1.5 for encryption/signing. + * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing) +{ + int byte_size = ctx->num_octets; + int num_pads_needed = byte_size-in_len-3; + bigint *dat_bi, *encrypt_bi; + + /* note: in_len+11 must be > byte_size */ + out_data[0] = 0; /* ensure encryption block is < modulus */ + + if (is_signing) + { + out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */ + memset(&out_data[2], 0xff, num_pads_needed); + } + else /* randomize the encryption padding with non-zero bytes */ + { + out_data[1] = 2; + get_random_NZ(num_pads_needed, &out_data[2]); + } + + out_data[2+num_pads_needed] = 0; + memcpy(&out_data[3+num_pads_needed], in_data, in_len); + + /* now encrypt it */ + dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size); + encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) : + RSA_public(ctx, dat_bi); + bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size); + + /* save a few bytes of memory */ + bi_clear_cache(ctx->bi_ctx); + return byte_size; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ diff --git a/libs/nixio/axTLS/crypto/sha1.c b/libs/nixio/axTLS/crypto/sha1.c new file mode 100644 index 000000000..be1910079 --- /dev/null +++ b/libs/nixio/axTLS/crypto/sha1.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995. + * This code was originally taken from RFC3174 + */ + +#include +#include "crypto.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* ----- static functions ----- */ +static void SHA1PadMessage(SHA1_CTX *ctx); +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx); + +/** + * Initialize the SHA1 context + */ +void SHA1_Init(SHA1_CTX *ctx) +{ + ctx->Length_Low = 0; + ctx->Length_High = 0; + ctx->Message_Block_Index = 0; + ctx->Intermediate_Hash[0] = 0x67452301; + ctx->Intermediate_Hash[1] = 0xEFCDAB89; + ctx->Intermediate_Hash[2] = 0x98BADCFE; + ctx->Intermediate_Hash[3] = 0x10325476; + ctx->Intermediate_Hash[4] = 0xC3D2E1F0; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +void SHA1_Update(SHA1_CTX *ctx, const uint8_t *msg, int len) +{ + while (len--) + { + ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF); + ctx->Length_Low += 8; + + if (ctx->Length_Low == 0) + ctx->Length_High++; + + if (ctx->Message_Block_Index == 64) + SHA1ProcessMessageBlock(ctx); + + msg++; + } +} + +/** + * Return the 160-bit message digest into the user's array + */ +void SHA1_Final(uint8_t *digest, SHA1_CTX *ctx) +{ + int i; + + SHA1PadMessage(ctx); + memset(ctx->Message_Block, 0, 64); + ctx->Length_Low = 0; /* and clear length */ + ctx->Length_High = 0; + + for (i = 0; i < SHA1_SIZE; i++) + { + digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ); + } +} + +/** + * Process the next 512 bits of the message stored in the array. + */ +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) + { + W[t] = ctx->Message_Block[t * 4] << 24; + W[t] |= ctx->Message_Block[t * 4 + 1] << 16; + W[t] |= ctx->Message_Block[t * 4 + 2] << 8; + W[t] |= ctx->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->Intermediate_Hash[0]; + B = ctx->Intermediate_Hash[1]; + C = ctx->Intermediate_Hash[2]; + D = ctx->Intermediate_Hash[3]; + E = ctx->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + ctx->Intermediate_Hash[0] += A; + ctx->Intermediate_Hash[1] += B; + ctx->Intermediate_Hash[2] += C; + ctx->Intermediate_Hash[3] += D; + ctx->Intermediate_Hash[4] += E; + ctx->Message_Block_Index = 0; +} + +/* + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * @param ctx [in, out] The SHA1 context + */ +static void SHA1PadMessage(SHA1_CTX *ctx) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (ctx->Message_Block_Index > 55) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 64) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(ctx); + + while (ctx->Message_Block_Index < 56) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + else + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 56) + { + + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + ctx->Message_Block[56] = ctx->Length_High >> 24; + ctx->Message_Block[57] = ctx->Length_High >> 16; + ctx->Message_Block[58] = ctx->Length_High >> 8; + ctx->Message_Block[59] = ctx->Length_High; + ctx->Message_Block[60] = ctx->Length_Low >> 24; + ctx->Message_Block[61] = ctx->Length_Low >> 16; + ctx->Message_Block[62] = ctx->Length_Low >> 8; + ctx->Message_Block[63] = ctx->Length_Low; + SHA1ProcessMessageBlock(ctx); +} diff --git a/libs/nixio/axTLS/docsrc/Makefile b/libs/nixio/axTLS/docsrc/Makefile new file mode 100644 index 000000000..28266869b --- /dev/null +++ b/libs/nixio/axTLS/docsrc/Makefile @@ -0,0 +1,39 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../config/makefile.conf + +all: + +doco: + doxygen ./axTLS.dox + +clean:: + @-rm -fr html *~ diff --git a/libs/nixio/axTLS/docsrc/axTLS.dox b/libs/nixio/axTLS/docsrc/axTLS.dox new file mode 100644 index 000000000..d9959b21d --- /dev/null +++ b/libs/nixio/axTLS/docsrc/axTLS.dox @@ -0,0 +1,1237 @@ +# Doxyfile 1.4.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a 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 (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = axTLS + +# 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 = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# 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 = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 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 cause performance problems for the file system. + +CREATE_SUBDIRS = 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. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) 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. + +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" "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. + +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. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then 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. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then 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. + +STRIP_FROM_PATH = + +# 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 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. + +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 +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_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 behaviour. +# 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 behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +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. + +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. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" 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:". +# You can put \n's in the value part of an alias to insert newlines. + +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. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# 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); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# 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. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) 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. + +SUBGROUPING = 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 and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# 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. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When 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 (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) 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. + +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 (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +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 (the default) these blocks will be appended to the +# function's detailed documentation block. + +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 (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = 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 default), 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. + +SORT_BY_SCOPE_NAME = 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. + +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. + +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. + +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. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of 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 initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# 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. + +SHOW_USED_FILES = NO + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is YES. + +SHOW_DIRECTORIES = NO + +# 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 , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED 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. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = 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) + +WARN_FORMAT = "$file:$line: $text" + +# 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 stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be 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. + +INPUT = ../bindings/csharp/axTLS.cs ../bindings/java/SSL.java ../bindings/java/SSLUtil.java ../bindings/java/SSLCTX.java ../bindings/java/SSLServer.java ../bindings/java/SSLClient.java ../bindings/java/SSLReadHolder.java ../ssl/ssl.h ../crypto/bigint.c ../crypto/bigint.h + +# If the value of the INPUT tag contains directories, you can use the +# FILE_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 the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# 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. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +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 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. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = images + +# 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. + +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 +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# 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 also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = 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 http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) 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. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# 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. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# 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. If left blank `html' will be used as the default path. + +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). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = doco_footer.html + +# 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 the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, 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. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = YES + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag 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 (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# 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. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = CONFIG_SSL_CERT_VERIFICATION CONFIG_SSL_ENABLE_CLIENT CONFIG_SSL_GENERATE_X509_CERT CONFIG_BIGINT_MONTGOMERY CONFIG_BIGINT_BARRETT CONFIG_BIGINT_CRT EXP_FUNC="" STDCALL="" + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/libs/nixio/axTLS/docsrc/doco_footer.html b/libs/nixio/axTLS/docsrc/doco_footer.html new file mode 100644 index 000000000..20c4e70b7 --- /dev/null +++ b/libs/nixio/axTLS/docsrc/doco_footer.html @@ -0,0 +1,3 @@ +

+

+Copyright © 2007 Cameron Rich diff --git a/libs/nixio/axTLS/docsrc/images/axolotl.jpg b/libs/nixio/axTLS/docsrc/images/axolotl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7352bbae836739e469c837a7fdb8114f786b2130 GIT binary patch literal 3041 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 literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/httpd/Config.in b/libs/nixio/axTLS/httpd/Config.in new file mode 100644 index 000000000..513d57ae3 --- /dev/null +++ b/libs/nixio/axTLS/httpd/Config.in @@ -0,0 +1,163 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/config/Kconfig-language.txt +# + +menu "Axhttpd Configuration" +depends on CONFIG_AXHTTPD + +config CONFIG_HTTP_STATIC_BUILD + bool "Static Build" + default n + help + Select y if you want axhttpd to be a static build (i.e. don't use the + axtls shared library or dll). + +config CONFIG_HTTP_PORT + int "HTTP port" + default 80 + help + The port number of the normal HTTP server. + + You must be a root user in order to use the default port. + +config CONFIG_HTTP_HTTPS_PORT + int "HTTPS port" + default 443 + help + The port number of the HTTPS server. + + You must be a root user in order to use the default port. + +config CONFIG_HTTP_SESSION_CACHE_SIZE + int "SSL session cache size" + default 5 + help + The size of the SSL session cache. + + This is not actually related to the number of concurrent users, but + for optimum performance they should be the same (with a penalty + in memory usage). + +config CONFIG_HTTP_WEBROOT + string "Web root location" + default "../www" if !CONFIG_PLATFORM_WIN32 + default "..\\www" if CONFIG_PLATFORM_WIN32 + help + The location of the web root in relation to axhttpd. This is + the directory where index.html lives. + +config CONFIG_HTTP_TIMEOUT + int "Timeout" + default 300 + help + Set the timeout of a connection in seconds. + +menu "CGI" +depends on !CONFIG_PLATFORM_WIN32 + +config CONFIG_HTTP_HAS_CGI + bool "Enable CGI" + default y + depends on !CONFIG_PLATFORM_WIN32 + help + Enable the CGI capability. Not available on Win32 platforms. + +config CONFIG_HTTP_CGI_EXTENSIONS + string "CGI File Extension(s)" + default ".lua,.lp" + depends on CONFIG_HTTP_HAS_CGI + help + Tell axhhtpd what file extension(s) are used for CGI. + + This is a comma separated list - e.g. ".php,.pl" etc + +config CONFIG_HTTP_ENABLE_LUA + bool "Enable Lua" + default y + depends on CONFIG_HTTP_HAS_CGI + help + Lua is a powerful, fast, light-weight, embeddable scripting language. + + See http://www.lua.org for details. + +config CONFIG_HTTP_LUA_PREFIX + string "Lua's Installation Prefix" + default "/usr/local" + depends on CONFIG_HTTP_ENABLE_LUA + + help + The location of Lua's installation prefix. This is also necessary for + Lua's cgi launcher application. + +config CONFIG_HTTP_LUA_CGI_LAUNCHER + string "CGI launcher location" + default "/bin/cgi.exe" if CONFIG_PLATFORM_CYGWIN + default "/bin/cgi" if !CONFIG_PLATFORM_CYGWIN + depends on CONFIG_HTTP_ENABLE_LUA + help + The location of LUA's CGI launcher application (after + the CONFIG_HTTP_LUA_PREFIX) + +config CONFIG_HTTP_BUILD_LUA + bool "Build Lua" + default n + depends on CONFIG_HTTP_ENABLE_LUA + help + Build Lua and install in /usr/local/bin + +endmenu + +config CONFIG_HTTP_DIRECTORIES + bool "Enable Directory Listing" + default y + help + Enable directory listing. + +config CONFIG_HTTP_HAS_AUTHORIZATION + bool "Enable authorization" + default y + help + Pages/directories can have passwords associated with them. + +config CONFIG_HTTP_HAS_IPV6 + bool "Enable IPv6" + default n + depends on !CONFIG_PLATFORM_WIN32 + help + Use IPv6 instead of IPv4. + + Does not work under Win32 + +config CONFIG_HTTP_ENABLE_DIFFERENT_USER + bool "Enable different user" + default n + depends on !CONFIG_PLATFORM_WIN32 + help + Allow the web server to be run as a different user + +config CONFIG_HTTP_USER + string "As User" + default "nobody" + depends on CONFIG_HTTP_ENABLE_DIFFERENT_USER + help + The user name that will be used to run axhttpd. + +config CONFIG_HTTP_VERBOSE + bool "Verbose Mode" + default y if CONFIG_SSL_FULL_MODE + default n if !CONFIG_SSL_FULL_MODE + help + Enable extra statements used when using axhttpd. + +config CONFIG_HTTP_IS_DAEMON + bool "Run as a daemon" + default n + depends on !CONFIG_PLATFORM_WIN32 + help + Run axhttpd as a background process. + + Does not work under Win32 + +endmenu + diff --git a/libs/nixio/axTLS/httpd/Makefile b/libs/nixio/axTLS/httpd/Makefile new file mode 100644 index 000000000..b8c18d244 --- /dev/null +++ b/libs/nixio/axTLS/httpd/Makefile @@ -0,0 +1,127 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +all : web_server lua + +AXTLS_HOME=.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +ifndef CONFIG_PLATFORM_WIN32 + +ifdef CONFIG_PLATFORM_CYGWIN +TARGET=$(AXTLS_HOME)/$(STAGE)/axhttpd.exe +TARGET2=$(AXTLS_HOME)/$(STAGE)/htpasswd.exe +else +TARGET=$(AXTLS_HOME)/$(STAGE)/axhttpd +TARGET2=$(AXTLS_HOME)/$(STAGE)/htpasswd +endif + +ifdef CONFIG_HTTP_STATIC_BUILD +LIBS=$(AXTLS_HOME)/$(STAGE)/libaxtls.a +else +LIBS=-L$(AXTLS_HOME)/$(STAGE) -laxtls +endif + +ifdef CONFIG_HTTP_BUILD_LUA +lua: kepler-1.1 + +kepler-1.1: + @tar xvfz kepler-1.1-snapshot-20070521-1825.tar.gz + @cat kepler.patch | patch -p0 + cd kepler-1.1; ./configure --prefix=$(CONFIG_HTTP_LUA_PREFIX) --launcher=cgi --lua-suffix= ; make install +else +lua: +endif + +else # win32 build +lua: + +TARGET=$(AXTLS_HOME)/$(STAGE)/axhttpd.exe +TARGET2=$(AXTLS_HOME)/$(STAGE)/htpasswd.exe + +ifdef CONFIG_HTTP_STATIC_BUILD +LIBS=$(AXTLS_HOME)/$(STAGE)/axtls.static.lib $(AXTLS_HOME)\\config\\axtls.res +else +LIBS=$(AXTLS_HOME)/$(STAGE)/axtls.lib $(AXTLS_HOME)\\config\\axtls.res +endif +endif + +ifndef CONFIG_AXHTTPD +web_server: +else + +web_server :: $(TARGET) + +ifdef CONFIG_HTTP_HAS_AUTHORIZATION +web_server :: $(TARGET2) +endif + +OBJ= \ + axhttpd.o \ + proc.o \ + tdate_parse.o + +include $(AXTLS_HOME)/config/makefile.post + +ifndef CONFIG_PLATFORM_WIN32 + +$(TARGET): $(OBJ) $(AXTLS_HOME)/$(STAGE)/libaxtls.a + $(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) +ifdef CONFIG_STRIP_UNWANTED_SECTIONS + $(STRIP) --remove-section=.comment $(TARGET) +endif + +$(TARGET2): htpasswd.o $(AXTLS_HOME)/$(STAGE)/libaxtls.a + $(LD) $(LDFLAGS) -o $@ htpasswd.o $(LIBS) + +else # Win32 + +OBJ:=$(OBJ:.o=.obj) +%.obj : %.c + $(CC) $(CFLAGS) $< + +htpasswd.obj : htpasswd.c + $(CC) $(CFLAGS) $? + +$(TARGET): $(OBJ) + $(LD) $(LDFLAGS) /out:$@ $(LIBS) $? + +$(TARGET2): htpasswd.obj + $(LD) $(LDFLAGS) /out:$@ $(LIBS) $? +endif + +endif # CONFIG_AXHTTPD + +clean:: + -@rm -f $(TARGET)* + -@rm -fr kepler-1.1 + diff --git a/libs/nixio/axTLS/httpd/axhttp.h b/libs/nixio/axTLS/httpd/axhttp.h new file mode 100644 index 000000000..73c299fb1 --- /dev/null +++ b/libs/nixio/axTLS/httpd/axhttp.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include "ssl.h" + +#define BACKLOG 15 +#define VERSION "1.0.0" +#ifdef CONFIG_HTTP_HAS_IPV6 +#define HAVE_IPV6 +#endif + +#define MAXPOSTDATASIZE 30000 +#define MAXREQUESTLENGTH 256 +#define BLOCKSIZE 4096 + +#define INITIAL_CONNECTION_SLOTS 10 +#define CONFIG_HTTP_DEFAULT_SSL_OPTIONS SSL_DISPLAY_CERTS + +#define STATE_WANT_TO_READ_HEAD 1 +#define STATE_WANT_TO_SEND_HEAD 2 +#define STATE_WANT_TO_READ_FILE 3 +#define STATE_WANT_TO_SEND_FILE 4 +#define STATE_DOING_DIR 5 + +enum +{ + TYPE_GET, + TYPE_HEAD, + TYPE_POST +}; + +struct connstruct +{ + struct connstruct *next; + int state; + int reqtype; + int networkdesc; + int filedesc; + SSL *ssl; + +#if defined(CONFIG_HTTP_DIRECTORIES) +#ifdef WIN32 + HANDLE dirp; + WIN32_FIND_DATA file_data; +#else + DIR *dirp; +#endif +#endif + + time_t timeout; + char actualfile[MAXREQUESTLENGTH]; + char filereq[MAXREQUESTLENGTH]; + char dirname[MAXREQUESTLENGTH]; + char server_name[MAXREQUESTLENGTH]; + int numbytes; + char databuf[BLOCKSIZE]; + uint8_t is_ssl; + uint8_t close_when_done; + time_t if_modified_since; + +#if defined(CONFIG_HTTP_HAS_CGI) + uint8_t is_cgi; +#ifdef CONFIG_HTTP_ENABLE_LUA + uint8_t is_lua; +#endif + int content_length; + char remote_addr[MAXREQUESTLENGTH]; + char uri_request[MAXREQUESTLENGTH]; + char uri_path_info[MAXREQUESTLENGTH]; + char uri_query[MAXREQUESTLENGTH]; + char cookie[MAXREQUESTLENGTH]; +#endif +#if defined(CONFIG_HTTP_HAS_AUTHORIZATION) + char authorization[MAXREQUESTLENGTH]; +#endif + int post_read; + int post_state; + char *post_data; +}; + +struct serverstruct +{ + struct serverstruct *next; + int sd; + int is_ssl; + SSL_CTX *ssl_ctx; +}; + +#if defined(CONFIG_HTTP_HAS_CGI) +struct cgiextstruct +{ + struct cgiextstruct *next; + char *ext; +}; +#endif + +/* global prototypes */ +extern struct serverstruct *servers; +extern struct connstruct *usedconns; +extern struct connstruct *freeconns; +extern const char * const server_version; + +#if defined(CONFIG_HTTP_HAS_CGI) +extern struct cgiextstruct *cgiexts; +#endif + +/* conn.c prototypes */ +void removeconnection(struct connstruct *cn); + +/* proc.c prototypes */ +void procdodir(struct connstruct *cn); +void procreadhead(struct connstruct *cn); +void procsendhead(struct connstruct *cn); +void procreadfile(struct connstruct *cn); +void procsendfile(struct connstruct *cn); +#if defined(CONFIG_HTTP_HAS_CGI) +void read_post_data(struct connstruct *cn); +#endif + +/* misc.c prototypes */ +char *my_strncpy(char *dest, const char *src, size_t n); +int isdir(const char *name); + +/* tdate prototypes */ +void tdate_init(void); +time_t tdate_parse(const char* str); + diff --git a/libs/nixio/axTLS/httpd/axhttpd.c b/libs/nixio/axTLS/httpd/axhttpd.c new file mode 100644 index 000000000..35d36f64c --- /dev/null +++ b/libs/nixio/axTLS/httpd/axhttpd.c @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "axhttp.h" + +struct serverstruct *servers; +struct connstruct *usedconns; +struct connstruct *freeconns; +const char * const server_version = "axhttpd/"AXTLS_VERSION; + +static void addtoservers(int sd); +static int openlistener(int port); +static void handlenewconnection(int listenfd, int is_ssl); +static void addconnection(int sd, char *ip, int is_ssl); +static void ax_chdir(void); + +#if defined(CONFIG_HTTP_HAS_CGI) +struct cgiextstruct *cgiexts; +static void addcgiext(const char *tp); + +#if !defined(WIN32) +static void reaper(int sigtype) +{ + wait3(NULL, WNOHANG, NULL); +} +#endif +#endif + +#ifdef CONFIG_HTTP_VERBOSE /* should really be in debug mode or something */ +/* clean up memory for valgrind */ +static void sigint_cleanup(int sig) +{ + struct serverstruct *sp; + struct connstruct *tp; + + while (servers != NULL) + { + if (servers->is_ssl) + ssl_ctx_free(servers->ssl_ctx); + + sp = servers->next; + free(servers); + servers = sp; + } + + while (freeconns != NULL) + { + tp = freeconns->next; + free(freeconns); + freeconns = tp; + } + + while (usedconns != NULL) + { + tp = usedconns->next; + free(usedconns); + usedconns = tp; + } + +#if defined(CONFIG_HTTP_HAS_CGI) + while (cgiexts) + { + struct cgiextstruct *cp = cgiexts->next; + if (cp == NULL) /* last entry */ + free(cgiexts->ext); + free(cgiexts); + cgiexts = cp; + } +#endif + + exit(0); +} + +static void die(int sigtype) +{ + exit(0); +} +#endif + +int main(int argc, char *argv[]) +{ + fd_set rfds, wfds; + struct connstruct *tp, *to; + struct serverstruct *sp; + int rnum, wnum, active; + int i; + time_t currtime; + +#ifdef WIN32 + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested,&wsaData); +#else + signal(SIGPIPE, SIG_IGN); +#if defined(CONFIG_HTTP_HAS_CGI) + signal(SIGCHLD, reaper); +#endif +#ifdef CONFIG_HTTP_VERBOSE + signal(SIGQUIT, die); +#endif +#endif + +#ifdef CONFIG_HTTP_VERBOSE + signal(SIGTERM, die); + signal(SIGINT, sigint_cleanup); +#endif + tdate_init(); + + for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++) + { + tp = freeconns; + freeconns = (struct connstruct *)calloc(1, sizeof(struct connstruct)); + freeconns->next = tp; + } + + if ((active = openlistener(CONFIG_HTTP_PORT)) == -1) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "ERR: Couldn't bind to port %d\n", + CONFIG_HTTP_PORT); +#endif + exit(1); + } + + addtoservers(active); + + if ((active = openlistener(CONFIG_HTTP_HTTPS_PORT)) == -1) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "ERR: Couldn't bind to port %d\n", + CONFIG_HTTP_HTTPS_PORT); +#endif + exit(1); + } + + addtoservers(active); + servers->ssl_ctx = ssl_ctx_new(CONFIG_HTTP_DEFAULT_SSL_OPTIONS, + CONFIG_HTTP_SESSION_CACHE_SIZE); + servers->is_ssl = 1; + +#if defined(CONFIG_HTTP_HAS_CGI) + addcgiext(CONFIG_HTTP_CGI_EXTENSIONS); +#endif + +#if defined(CONFIG_HTTP_VERBOSE) +#if defined(CONFIG_HTTP_HAS_CGI) + printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS); +#endif + printf("%s: listening on ports %d (http) and %d (https)\n", + server_version, CONFIG_HTTP_PORT, CONFIG_HTTP_HTTPS_PORT); + TTY_FLUSH(); +#endif + + ax_chdir(); + +#ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER + { + struct passwd *pd = getpwnam(CONFIG_HTTP_USER); + + if (pd != NULL) + { + int res = setuid(pd->pw_uid); + res |= setgid(pd->pw_gid); + +#if defined(CONFIG_HTTP_VERBOSE) + if (res == 0) + { + printf("change to '%s' successful\n", CONFIG_HTTP_USER); + TTY_FLUSH(); + } +#endif + } + + } +#endif + + +#ifndef WIN32 +#ifdef CONFIG_HTTP_IS_DAEMON + if (fork() > 0) /* parent will die */ + exit(0); + + setsid(); +#endif +#endif + + /* main loop */ + while (1) + { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + rnum = wnum = -1; + sp = servers; + + while (sp != NULL) /* read each server port */ + { + FD_SET(sp->sd, &rfds); + + if (sp->sd > rnum) + rnum = sp->sd; + sp = sp->next; + } + + /* Add the established sockets */ + tp = usedconns; + currtime = time(NULL); + + while (tp != NULL) + { + if (currtime > tp->timeout) /* timed out? Kill it. */ + { + to = tp; + tp = tp->next; + removeconnection(to); + continue; + } + + if (tp->state == STATE_WANT_TO_READ_HEAD) + { + FD_SET(tp->networkdesc, &rfds); + if (tp->networkdesc > rnum) + rnum = tp->networkdesc; + } + + if (tp->state == STATE_WANT_TO_SEND_HEAD) + { + FD_SET(tp->networkdesc, &wfds); + if (tp->networkdesc > wnum) + wnum = tp->networkdesc; + } + + if (tp->state == STATE_WANT_TO_READ_FILE) + { + FD_SET(tp->filedesc, &rfds); + if (tp->filedesc > rnum) + rnum = tp->filedesc; + } + + if (tp->state == STATE_WANT_TO_SEND_FILE) + { + FD_SET(tp->networkdesc, &wfds); + if (tp->networkdesc > wnum) + wnum = tp->networkdesc; + } + +#if defined(CONFIG_HTTP_DIRECTORIES) + if (tp->state == STATE_DOING_DIR) + { + FD_SET(tp->networkdesc, &wfds); + if (tp->networkdesc > wnum) + wnum = tp->networkdesc; + } +#endif + tp = tp->next; + } + + active = select(wnum > rnum ? wnum+1 : rnum+1, + rnum != -1 ? &rfds : NULL, + wnum != -1 ? &wfds : NULL, + NULL, NULL); + + /* New connection? */ + sp = servers; + while (active > 0 && sp != NULL) + { + if (FD_ISSET(sp->sd, &rfds)) + { + handlenewconnection(sp->sd, sp->is_ssl); + active--; + } + + sp = sp->next; + } + + /* Handle the established sockets */ + tp = usedconns; + + while (active > 0 && tp != NULL) + { + to = tp; + tp = tp->next; + + if (to->state == STATE_WANT_TO_READ_HEAD && + FD_ISSET(to->networkdesc, &rfds)) + { + active--; +#if defined(CONFIG_HTTP_HAS_CGI) + if (to->post_state) + read_post_data(to); + else +#endif + procreadhead(to); + } + + if (to->state == STATE_WANT_TO_SEND_HEAD && + FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procsendhead(to); + } + + if (to->state == STATE_WANT_TO_READ_FILE && + FD_ISSET(to->filedesc, &rfds)) + { + active--; + procreadfile(to); + } + + if (to->state == STATE_WANT_TO_SEND_FILE && + FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procsendfile(to); + } + +#if defined(CONFIG_HTTP_DIRECTORIES) + if (to->state == STATE_DOING_DIR && + FD_ISSET(to->networkdesc, &wfds)) + { + active--; + procdodir(to); + } +#endif + } + } + + return 0; +} + +#if defined(CONFIG_HTTP_HAS_CGI) +static void addcgiext(const char *cgi_exts) +{ + char *cp = strdup(cgi_exts); + + /* extenstions are comma separated */ + do + { + struct cgiextstruct *ex = (struct cgiextstruct *) + malloc(sizeof(struct cgiextstruct)); + ex->ext = cp; + ex->next = cgiexts; + cgiexts = ex; + if ((cp = strchr(cp, ',')) != NULL) + *cp++ = 0; + } while (cp != NULL); +} +#endif + +static void addtoservers(int sd) +{ + struct serverstruct *tp = (struct serverstruct *) + calloc(1, sizeof(struct serverstruct)); + tp->next = servers; + tp->sd = sd; + servers = tp; +} + +#ifdef HAVE_IPV6 +static void handlenewconnection(int listenfd, int is_ssl) +{ + struct sockaddr_in6 their_addr; + int tp = sizeof(their_addr); + char ipbuf[100]; + int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp); + + if (tp == sizeof(struct sockaddr_in6)) + inet_ntop(AF_INET6, &their_addr.sin6_addr, ipbuf, sizeof(ipbuf)); + else if (tp == sizeof(struct sockaddr_in)) + inet_ntop(AF_INET, &(((struct sockaddr_in *)&their_addr)->sin_addr), + ipbuf, sizeof(ipbuf)); + else + *ipbuf = '\0'; + + addconnection(connfd, ipbuf, is_ssl); +} + +#else +static void handlenewconnection(int listenfd, int is_ssl) +{ + struct sockaddr_in their_addr; + socklen_t tp = sizeof(struct sockaddr_in); + int connfd = accept(listenfd, (struct sockaddr *)&their_addr, &tp); + addconnection(connfd, inet_ntoa(their_addr.sin_addr), is_ssl); +} +#endif + +static int openlistener(int port) +{ + int sd; +#ifdef WIN32 + char tp = 1; +#else + int tp = 1; +#endif +#ifndef HAVE_IPV6 + struct sockaddr_in my_addr; + + if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return -1; + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons((short)port); + my_addr.sin_addr.s_addr = INADDR_ANY; +#else + struct sockaddr_in6 my_addr; + + if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) + return -1; + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin6_family = AF_INET6; + my_addr.sin6_port = htons(port); + my_addr.sin6_addr.s_addr = INADDR_ANY; +#endif + + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &tp, sizeof(tp)); + if (bind(sd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) + { + close(sd); + return -1; + } + + listen(sd, BACKLOG); + return sd; +} + +/* Wrapper function for strncpy() that guarantees + a null-terminated string. This is to avoid any possible + issues due to strncpy()'s behaviour. + */ +char *my_strncpy(char *dest, const char *src, size_t n) +{ + strncpy(dest, src, n); + dest[n-1] = '\0'; + return dest; +} + +int isdir(const char *tpbuf) +{ + struct stat st; + char path[MAXREQUESTLENGTH]; + strcpy(path, tpbuf); + +#ifdef WIN32 /* win32 stat() can't handle trailing '\' */ + if (path[strlen(path)-1] == '\\') + path[strlen(path)-1] = 0; +#endif + + if (stat(path, &st) == -1) + return 0; + + if ((st.st_mode & S_IFMT) == S_IFDIR) + return 1; + + return 0; +} + +static void addconnection(int sd, char *ip, int is_ssl) +{ + struct connstruct *tp; + + /* Get ourselves a connstruct */ + if (freeconns == NULL) + tp = (struct connstruct *)calloc(1, sizeof(struct connstruct)); + else + { + tp = freeconns; + freeconns = tp->next; + } + + /* Attach it to the used list */ + tp->next = usedconns; + usedconns = tp; + tp->networkdesc = sd; + + if (is_ssl) + tp->ssl = ssl_server_new(servers->ssl_ctx, sd); + + tp->is_ssl = is_ssl; + tp->filedesc = -1; +#if defined(CONFIG_HTTP_HAS_DIRECTORIES) + tp->dirp = NULL; +#endif + *tp->actualfile = '\0'; + *tp->filereq = '\0'; + tp->state = STATE_WANT_TO_READ_HEAD; + tp->reqtype = TYPE_GET; + tp->close_when_done = 0; + tp->timeout = time(NULL) + CONFIG_HTTP_TIMEOUT; +#if defined(CONFIG_HTTP_HAS_CGI) + strcpy(tp->remote_addr, ip); +#endif +} + +void removeconnection(struct connstruct *cn) +{ + struct connstruct *tp; + int shouldret = 0; + + tp = usedconns; + + if (tp == NULL || cn == NULL) + shouldret = 1; + else if (tp == cn) + usedconns = tp->next; + else + { + while (tp != NULL) + { + if (tp->next == cn) + { + tp->next = (tp->next)->next; + shouldret = 0; + break; + } + + tp = tp->next; + shouldret = 1; + } + } + + if (shouldret) + return; + + /* If we did, add it to the free list */ + cn->next = freeconns; + freeconns = cn; + + /* Close it all down */ + if (cn->networkdesc != -1) + { + if (cn->is_ssl) + { + ssl_free(cn->ssl); + cn->ssl = NULL; + } + + SOCKET_CLOSE(cn->networkdesc); + } + + if (cn->filedesc != -1) + close(cn->filedesc); + +#if defined(CONFIG_HTTP_HAS_DIRECTORIES) + if (cn->dirp != NULL) +#ifdef WIN32 + FindClose(cn->dirp); +#else + closedir(cn->dirp); +#endif +#endif +} + +/* + * Change directories one way or the other. + */ +static void ax_chdir(void) +{ + static char *webroot = CONFIG_HTTP_WEBROOT; + + if (chdir(webroot)) + { +#ifdef CONFIG_HTTP_VERBOSE + fprintf(stderr, "'%s' is not a directory\n", webroot); +#endif + exit(1); + } +} + diff --git a/libs/nixio/axTLS/httpd/htpasswd.c b/libs/nixio/axTLS/httpd/htpasswd.c new file mode 100644 index 000000000..1a7a2311f --- /dev/null +++ b/libs/nixio/axTLS/httpd/htpasswd.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include +#include +#include +#include "ssl.h" + +int tfd; + +void base64_encode(const uint8_t *in, size_t inlen, char *out, size_t outlen) +{ + static const char b64str[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + while (inlen && outlen) + { + *out++ = b64str[(in[0] >> 2) & 0x3f]; + if (!--outlen) + break; + + *out++ = b64str[((in[0] << 4) + + (--inlen ? in[1] >> 4 : 0)) & 0x3f]; + if (!--outlen) + break; + *out++ = (inlen + ? b64str[((in[1] << 2) + + (--inlen ? in[2] >> 6 : 0)) + & 0x3f] + : '='); + if (!--outlen) + break; + *out++ = inlen ? b64str[in[2] & 0x3f] : '='; + if (!--outlen) + break; + if (inlen) + inlen--; + if (inlen) + in += 3; + } + + if (outlen) + *out = '\0'; +} + +static void usage(void) +{ + fprintf(stderr,"Usage: htpasswd username\n"); + exit(1); +} + +#ifdef WIN32 +static char * getpass(const char *prompt) +{ + static char buf[127]; + FILE *fp = stdin; + + printf(prompt); TTY_FLUSH(); +#if 0 + fp = fopen("/dev/tty", "w"); + if (fp == NULL) + { + printf("null\n"); TTY_FLUSH(); + fp = stdin; + } +#endif + + fgets(buf, sizeof(buf), fp); + while (buf[strlen(buf)-1] < ' ') + buf[strlen(buf)-1] = '\0'; + + //if (fp != stdin) + // fclose(fp); + return buf; +} +#endif + +int main(int argc, char *argv[]) +{ + char* pw; + uint8_t md5_salt[MD5_SIZE], md5_pass[MD5_SIZE]; + char b64_salt[MD5_SIZE+10], b64_pass[MD5_SIZE+10]; + MD5_CTX ctx; + + if (argc != 2) + usage(); + + pw = strdup(getpass("New password:")); + if (strcmp(pw, getpass("Re-type new password:")) != 0) + { + fprintf(stderr, "They don't match, sorry.\n" ); + exit(1); + } + + RNG_initialize((uint8_t *)pw, sizeof(pw)); + get_random(MD5_SIZE, md5_salt); + RNG_terminate(); + base64_encode(md5_salt, MD5_SIZE, b64_salt, sizeof(b64_salt)); + + MD5_Init(&ctx); + MD5_Update(&ctx, md5_salt, MD5_SIZE); + MD5_Update(&ctx, (uint8_t *)pw, strlen(pw)); + MD5_Final(md5_pass, &ctx); + base64_encode(md5_pass, MD5_SIZE, b64_pass, sizeof(b64_pass)); + + printf("Add the following to your '.htpasswd' file\n"); + printf("%s:%s$%s\n", argv[1], b64_salt, b64_pass); + return 0; +} diff --git a/libs/nixio/axTLS/httpd/kepler-1.1-snapshot-20070521-1825.tar.gz b/libs/nixio/axTLS/httpd/kepler-1.1-snapshot-20070521-1825.tar.gz new file mode 100755 index 0000000000000000000000000000000000000000..d1caec5a53f93f043a32ec04807dff259223c32c GIT binary patch literal 768249 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 YXxC^qAC}G&Q`96+aR{7@ixnC?xYwp`@XY^ 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�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

2bm+X^54>e7*eCs3InBgzoH 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

+%Mc3Se$=H}jAx->R7 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

7vILk*vAofp%dpOsD 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 +#include +#include +#include +#include +#include +#include +#include +#include "axhttp.h" + +#define HTTP_VERSION "HTTP/1.1" + +static const char * index_file = "index.html"; + +static int special_read(struct connstruct *cn, void *buf, size_t count); +static int special_write(struct connstruct *cn, + const char *buf, size_t count); +static void send_error(struct connstruct *cn, int err); +static int hexit(char c); +static void urldecode(char *buf); +static void buildactualfile(struct connstruct *cn); +static int sanitizefile(const char *buf); +static int sanitizehost(char *buf); +static int htaccess_check(struct connstruct *cn); +static const char *getmimetype(const char *name); + +#if defined(CONFIG_HTTP_DIRECTORIES) +static void urlencode(const uint8_t *s, char *t); +static void procdirlisting(struct connstruct *cn); +#endif +#if defined(CONFIG_HTTP_HAS_CGI) +static void proccgi(struct connstruct *cn); +static void decode_path_info(struct connstruct *cn, char *path_info); +static int init_read_post_data(char *buf, char *data, struct connstruct *cn, int old_rv); +#endif +#ifdef CONFIG_HTTP_HAS_AUTHORIZATION +static int auth_check(struct connstruct *cn); +#endif + +#if AXDEBUG +#define AXDEBUGSTART \ + { \ + FILE *axdout; \ + axdout = fopen("/var/log/axdebug", "a"); \ + +#define AXDEBUGEND \ + fclose(axdout); \ + } +#else /* AXDEBUG */ +#define AXDEBUGSTART +#define AXDEBUGEND +#endif /* AXDEBUG */ + +/* Returns 1 if elems should continue being read, 0 otherwise */ +static int procheadelem(struct connstruct *cn, char *buf) +{ + char *delim, *value; + + if ((delim = strchr(buf, ' ')) == NULL) + return 0; + + *delim = 0; + value = delim+1; + + if (strcmp(buf, "GET") == 0 || strcmp(buf, "HEAD") == 0 || + strcmp(buf, "POST") == 0) + { + if (buf[0] == 'H') + cn->reqtype = TYPE_HEAD; + else if (buf[0] == 'P') + cn->reqtype = TYPE_POST; + + if ((delim = strchr(value, ' ')) == NULL) /* expect HTTP type */ + return 0; + + *delim = 0; + urldecode(value); + + if (sanitizefile(value) == 0) + { + send_error(cn, 403); + return 0; + } + +#if defined(CONFIG_HTTP_HAS_CGI) + decode_path_info(cn, value); +#else + my_strncpy(cn->filereq, value, MAXREQUESTLENGTH); +#endif + cn->if_modified_since = -1; + } + else if (strcmp(buf, "Host:") == 0) + { + if (sanitizehost(value) == 0) + { + removeconnection(cn); + return 0; + } + + my_strncpy(cn->server_name, value, MAXREQUESTLENGTH); + } + else if (strcmp(buf, "Connection:") == 0 && strcmp(value, "close") == 0) + { + cn->close_when_done = 1; + } + else if (strcmp(buf, "If-Modified-Since:") == 0) + { + cn->if_modified_since = tdate_parse(value); + } + else if (strcmp(buf, "Expect:") == 0) + { + send_error(cn, 417); /* expectation failed */ + return 0; + } +#ifdef CONFIG_HTTP_HAS_AUTHORIZATION + else if (strcmp(buf, "Authorization:") == 0 && + strncmp(value, "Basic ", 6) == 0) + { + int size; + if (base64_decode(&value[6], strlen(&value[6]), + (uint8_t *)cn->authorization, &size)) + cn->authorization[0] = 0; /* error */ + else + cn->authorization[size] = 0; + } +#endif +#if defined(CONFIG_HTTP_HAS_CGI) + else if (strcmp(buf, "Content-Length:") == 0) + { + sscanf(value, "%d", &cn->content_length); + } + else if (strcmp(buf, "Cookie:") == 0) + { + my_strncpy(cn->cookie, value, MAXREQUESTLENGTH); + } +#endif + + return 1; +} + +#if defined(CONFIG_HTTP_DIRECTORIES) +static void procdirlisting(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH]; + char actualfile[1024]; + + if (cn->reqtype == TYPE_HEAD) + { + snprintf(buf, sizeof(buf), HTTP_VERSION + " 200 OK\nContent-Type: text/html\n\n"); + write(cn->networkdesc, buf, strlen(buf)); + removeconnection(cn); + return; + } + + strcpy(actualfile, cn->actualfile); + +#ifdef WIN32 + strcat(actualfile, "*"); + cn->dirp = FindFirstFile(actualfile, &cn->file_data); + + if (cn->dirp == INVALID_HANDLE_VALUE) + { + send_error(cn, 404); + return; + } +#else + if ((cn->dirp = opendir(actualfile)) == NULL) + { + send_error(cn, 404); + return; + } +#endif + + snprintf(buf, sizeof(buf), HTTP_VERSION + " 200 OK\nContent-Type: text/html\n\n" + "\nDirectory Listing\n" + "

Directory listing of %s://%s%s


\n", + cn->is_ssl ? "https" : "http", cn->server_name, cn->filereq); + special_write(cn, buf, strlen(buf)); + cn->state = STATE_DOING_DIR; +} + +void procdodir(struct connstruct *cn) +{ +#ifndef WIN32 + struct dirent *dp; +#endif + char buf[MAXREQUESTLENGTH]; + char encbuf[1024]; + char *file; + + do + { + buf[0] = 0; + +#ifdef WIN32 + if (!FindNextFile(cn->dirp, &cn->file_data)) +#else + if ((dp = readdir(cn->dirp)) == NULL) +#endif + { + snprintf(buf, sizeof(buf), "\n"); + special_write(cn, buf, strlen(buf)); + removeconnection(cn); +#ifndef WIN32 + closedir(cn->dirp); +#endif + return; + } + +#ifdef WIN32 + file = cn->file_data.cFileName; +#else + file = dp->d_name; +#endif + + /* if no index file, don't display the ".." directory */ + if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' && + strcmp(file, "..") == 0) + continue; + + /* don't display files beginning with "." */ + if (file[0] == '.' && file[1] != '.') + continue; + + /* make sure a '/' is at the end of a directory */ + if (cn->filereq[strlen(cn->filereq)-1] != '/') + strcat(cn->filereq, "/"); + + /* see if the dir + file is another directory */ + snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file); + if (isdir(buf)) + strcat(file, "/"); + + urlencode((uint8_t *)file, encbuf); + snprintf(buf, sizeof(buf), "
%s
\n", + cn->filereq, encbuf, file); + } while (special_write(cn, buf, strlen(buf))); +} + +/* Encode funny chars -> %xx in newly allocated storage */ +/* (preserves '/' !) */ +static void urlencode(const uint8_t *s, char *t) +{ + const uint8_t *p = s; + char *tp = t; + + for (; *p; p++) + { + if ((*p > 0x00 && *p < ',') || + (*p > '9' && *p < 'A') || + (*p > 'Z' && *p < '_') || + (*p > '_' && *p < 'a') || + (*p > 'z' && *p < 0xA1)) + { + sprintf((char *)tp, "%%%02X", *p); + tp += 3; + } + else + { + *tp = *p; + tp++; + } + } + + *tp='\0'; +} + +#endif + +void procreadhead(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH*4], *tp, *next; + int rv; + + memset(buf, 0, MAXREQUESTLENGTH*4); + rv = special_read(cn, buf, sizeof(buf)-1); + if (rv <= 0) + { + if (rv < 0) /* really dead? */ + removeconnection(cn); + return; + } + + buf[rv] = '\0'; + next = tp = buf; + +#ifdef CONFIG_HTTP_HAS_AUTHORIZATION + cn->authorization[0] = 0; +#endif + + /* Split up lines and send to procheadelem() */ + while (*next != '\0') + { + /* If we have a blank line, advance to next stage */ + if (*next == '\r' || *next == '\n') + { +#if defined(CONFIG_HTTP_HAS_CGI) + if (cn->reqtype == TYPE_POST && cn->content_length > 0) + { + if (init_read_post_data(buf,next,cn,rv) == 0) + return; + } +#endif + + buildactualfile(cn); + cn->state = STATE_WANT_TO_SEND_HEAD; + return; + } + + while (*next != '\r' && *next != '\n' && *next != '\0') + next++; + + if (*next == '\r') + { + *next = '\0'; + next += 2; + } + else if (*next == '\n') + *next++ = '\0'; + + if (procheadelem(cn, tp) == 0) + return; + + tp = next; + } +} + +/* In this function we assume that the file has been checked for + * maliciousness (".."s, etc) and has been decoded + */ +void procsendhead(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH]; + struct stat stbuf; + time_t now = cn->timeout - CONFIG_HTTP_TIMEOUT; + char date[32]; + int file_exists; + + /* are we trying to access a file over the HTTP connection instead of a + * HTTPS connection? Or is this directory disabled? */ + if (htaccess_check(cn)) + { + send_error(cn, 403); + return; + } + +#ifdef CONFIG_HTTP_HAS_AUTHORIZATION + if (auth_check(cn)) /* see if there is a '.htpasswd' file */ + { +#ifdef CONFIG_HTTP_VERBOSE + printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH(); +#endif + removeconnection(cn); + return; + } +#endif + + file_exists = stat(cn->actualfile, &stbuf); + +#if defined(CONFIG_HTTP_HAS_CGI) + + if (file_exists != -1 && cn->is_cgi) + { + if ((stbuf.st_mode & S_IEXEC) == 0 || isdir(cn->actualfile)) + { + /* A non-executable file, or directory? */ + send_error(cn, 403); + } + else + proccgi(cn); + + return; + } +#endif + + /* look for "index.html"? */ + if (isdir(cn->actualfile)) + { + char tbuf[MAXREQUESTLENGTH]; + snprintf(tbuf, MAXREQUESTLENGTH, "%s%s", cn->actualfile, index_file); + + if ((file_exists = stat(tbuf, &stbuf)) != -1) + my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH); + else + { +#if defined(CONFIG_HTTP_DIRECTORIES) + /* If not, we do a directory listing of it */ + procdirlisting(cn); +#else + send_error(cn, 404); +#endif + return; + } + } + + if (file_exists == -1) + { + send_error(cn, 404); + return; + } + + strcpy(date, ctime(&now)); + + /* has the file been read before? */ + if (cn->if_modified_since != -1 && (cn->if_modified_since == 0 || + cn->if_modified_since >= stbuf.st_mtime)) + { + snprintf(buf, sizeof(buf), HTTP_VERSION" 304 Not Modified\nServer: " + "%s\nDate: %s\n", server_version, date); + special_write(cn, buf, strlen(buf)); + cn->state = STATE_WANT_TO_READ_HEAD; + return; + } + + if (cn->reqtype == TYPE_HEAD) + { + removeconnection(cn); + return; + } + else + { + int flags = O_RDONLY; +#if defined(WIN32) || defined(CONFIG_PLATFORM_CYGWIN) + flags |= O_BINARY; +#endif + cn->filedesc = open(cn->actualfile, flags); + + if (cn->filedesc < 0) + { + send_error(cn, 404); + return; + } + + snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\nServer: %s\n" + "Content-Type: %s\nContent-Length: %ld\n" + "Date: %sLast-Modified: %s\n", server_version, + getmimetype(cn->actualfile), (long) stbuf.st_size, + date, ctime(&stbuf.st_mtime)); /* ctime() has a \n on the end */ + + special_write(cn, buf, strlen(buf)); + +#ifdef CONFIG_HTTP_VERBOSE + printf("axhttpd: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq); + TTY_FLUSH(); +#endif + +#ifdef WIN32 + for (;;) + { + procreadfile(cn); + if (cn->filedesc == -1) + break; + + do + { + procsendfile(cn); + } while (cn->state != STATE_WANT_TO_READ_FILE); + } +#else + cn->state = STATE_WANT_TO_READ_FILE; +#endif + } +} + +void procreadfile(struct connstruct *cn) +{ + int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE); + + if (rv <= 0) + { + close(cn->filedesc); + cn->filedesc = -1; + + if (cn->close_when_done) /* close immediately */ + removeconnection(cn); + else + { /* keep socket open - HTTP 1.1 */ + cn->state = STATE_WANT_TO_READ_HEAD; + cn->numbytes = 0; + } + + return; + } + + cn->numbytes = rv; + cn->state = STATE_WANT_TO_SEND_FILE; +} + +void procsendfile(struct connstruct *cn) +{ + int rv = special_write(cn, cn->databuf, cn->numbytes); + + if (rv < 0) + removeconnection(cn); + else if (rv == cn->numbytes) + { + cn->state = STATE_WANT_TO_READ_FILE; + } + else if (rv == 0) + { + /* Do nothing */ + } + else + { + memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv); + cn->numbytes -= rv; + } +} + +#if defined(CONFIG_HTTP_HAS_CGI) +/* Should this be a bit more dynamic? It would mean more calls to malloc etc */ +#define CGI_ARG_SIZE 17 + +static void proccgi(struct connstruct *cn) +{ + int tpipe[2], spipe[2]; + char *myargs[2]; + char cgienv[CGI_ARG_SIZE][MAXREQUESTLENGTH]; + char * cgiptr[CGI_ARG_SIZE+4]; + const char *type = "HEAD"; + int cgi_index = 0, i; + pid_t pid; +#ifdef WIN32 + int tmp_stdout; +#endif + + snprintf(cgienv[0], MAXREQUESTLENGTH, + HTTP_VERSION" 200 OK\nServer: %s\n%s", + server_version, (cn->reqtype == TYPE_HEAD) ? "\n" : ""); + special_write(cn, cgienv[0], strlen(cgienv[0])); + + if (cn->reqtype == TYPE_HEAD) + { + removeconnection(cn); + return; + } + +#ifdef CONFIG_HTTP_VERBOSE + printf("[CGI]: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq); + TTY_FLUSH(); +#endif + + /* win32 cgi is a bit too painful */ +#ifndef WIN32 + /* set up pipe that is used for sending POST query data to CGI script*/ + if (cn->reqtype == TYPE_POST) + { + if (pipe(spipe) == -1) + { + printf("[CGI]: could not create pipe"); + TTY_FLUSH(); + return; + } + } + + if (pipe(tpipe) == -1) + { + printf("[CGI]: could not create pipe"); + TTY_FLUSH(); + return; + } + + /* + * use vfork() instead of fork() for performance + */ + if ((pid = vfork()) > 0) /* parent */ + { + /* Send POST query data to CGI script */ + if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0)) + { + write(spipe[1], cn->post_data, cn->content_length); + close(spipe[0]); + close(spipe[1]); + + /* free the memory that is allocated in read_post_data() */ + free(cn->post_data); + cn->post_data = NULL; + } + + /* Close the write descriptor */ + close(tpipe[1]); + cn->filedesc = tpipe[0]; + cn->state = STATE_WANT_TO_READ_FILE; + cn->close_when_done = 1; + return; + } + + if (pid < 0) /* vfork failed */ + exit(1); + + /* The problem child... */ + + /* Our stdout/stderr goes to the socket */ + dup2(tpipe[1], 1); + dup2(tpipe[1], 2); + + /* If it was a POST request, send the socket data to our stdin */ + if (cn->reqtype == TYPE_POST) + dup2(spipe[0], 0); + else /* Otherwise we can shutdown the read side of the sock */ + shutdown(cn->networkdesc, 0); + + myargs[0] = cn->actualfile; + myargs[1] = NULL; + + /* + * set the cgi args. A url is defined by: + * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING + * TODO: other CGI parameters? + */ + sprintf(cgienv[cgi_index++], "SERVER_SOFTWARE=%s", server_version); + strcpy(cgienv[cgi_index++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "SERVER_NAME=%s", cn->server_name); + sprintf(cgienv[cgi_index++], "SERVER_PORT=%d", + cn->is_ssl ? CONFIG_HTTP_HTTPS_PORT : CONFIG_HTTP_PORT); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "REQUEST_URI=%s", cn->uri_request); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "SCRIPT_NAME=%s", cn->filereq); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "PATH_INFO=%s", cn->uri_path_info); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "QUERY_STRING=%s", cn->uri_query); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "REMOTE_ADDR=%s", cn->remote_addr); + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "HTTP_COOKIE=%s", cn->cookie); /* note: small size */ +#if defined(CONFIG_HTTP_HAS_AUTHORIZATION) + snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, + "REMOTE_USER=%s", cn->authorization); +#endif + + switch (cn->reqtype) + { + case TYPE_GET: + type = "GET"; + break; + + case TYPE_POST: + type = "POST"; + sprintf(cgienv[cgi_index++], + "CONTENT_LENGTH=%d", cn->content_length); + strcpy(cgienv[cgi_index++], /* hard-code? */ + "CONTENT_TYPE=application/x-www-form-urlencoded"); + break; + } + + sprintf(cgienv[cgi_index++], "REQUEST_METHOD=%s", type); + + if (cn->is_ssl) + strcpy(cgienv[cgi_index++], "HTTPS=on"); + +#ifdef CONFIG_PLATFORM_CYGWIN + /* TODO: find out why Lua needs this */ + strcpy(cgienv[cgi_index++], "PATH=/usr/bin"); +#endif + + if (cgi_index >= CGI_ARG_SIZE) + { + printf("Content-type: text/plain\n\nToo many CGI args (%d, %d)\n", + cgi_index, CGI_ARG_SIZE); + _exit(1); + } + + /* copy across the pointer indexes */ + for (i = 0; i < cgi_index; i++) + cgiptr[i] = cgienv[i]; + + cgiptr[i++] = "AUTH_TYPE=Basic"; + cgiptr[i++] = "GATEWAY_INTERFACE=CGI/1.1"; + cgiptr[i++] = "SERVER_PROTOCOL="HTTP_VERSION; + cgiptr[i] = NULL; + + execve(myargs[0], myargs, cgiptr); + printf("Content-type: text/plain\n\nshouldn't get here\n"); + _exit(1); +#endif +} + +static char * cgi_filetype_match(struct connstruct *cn, const char *fn) +{ + struct cgiextstruct *tp = cgiexts; + + while (tp != NULL) + { + char *t; + + if ((t = strstr(fn, tp->ext)) != NULL) + { + t += strlen(tp->ext); + + if (*t == '/' || *t == '\0') + { +#ifdef CONFIG_HTTP_ENABLE_LUA + if (strcmp(tp->ext, ".lua") == 0 || strcmp(tp->ext, ".lp") == 0) + cn->is_lua = 1; +#endif + + return t; + } + else + return NULL; + + } + + tp = tp->next; + } + + return NULL; +} + +static void decode_path_info(struct connstruct *cn, char *path_info) +{ + char *cgi_delim; + + cn->is_cgi = 0; +#ifdef CONFIG_HTTP_ENABLE_LUA + cn->is_lua = 0; +#endif + *cn->uri_request = '\0'; + *cn->uri_path_info = '\0'; + *cn->uri_query = '\0'; + + my_strncpy(cn->uri_request, path_info, MAXREQUESTLENGTH); + + /* query info? */ + if ((cgi_delim = strchr(path_info, '?'))) + { + *cgi_delim = '\0'; + my_strncpy(cn->uri_query, cgi_delim+1, MAXREQUESTLENGTH); + } + + if ((cgi_delim = cgi_filetype_match(cn, path_info)) != NULL) + { + cn->is_cgi = 1; /* definitely a CGI script */ + + /* path info? */ + if (*cgi_delim != '\0') + { + my_strncpy(cn->uri_path_info, cgi_delim, MAXREQUESTLENGTH); + *cgi_delim = '\0'; + } + } + + /* the bit at the start must be the script name */ + my_strncpy(cn->filereq, path_info, MAXREQUESTLENGTH); +} + +static int init_read_post_data(char *buf, char *data, + struct connstruct *cn, int old_rv) +{ + char *next = data; + int rv = old_rv; + char *post_data; + + /* Too much Post data to send. MAXPOSTDATASIZE should be + configured (now it can be chaged in the header file) */ + if (cn->content_length > MAXPOSTDATASIZE) + { + send_error(cn, 418); + return 0; + } + + /* remove CRLF */ + while ((*next == '\r' || *next == '\n') && (next < &buf[rv])) + next++; + + if (cn->post_data == NULL) + { + cn->post_data = (char *) calloc(1, (cn->content_length + 1)); + /* Allocate buffer for the POST data that will be used by proccgi + to send POST data to the CGI script */ + + if (cn->post_data == NULL) + { + printf("axhttpd: could not allocate memory for POST data\n"); + TTY_FLUSH(); + send_error(cn, 599); + return 0; + } + } + + cn->post_state = 0; + cn->post_read = 0; + post_data = cn->post_data; + + while (next < &buf[rv]) + { + /*copy POST data to buffer*/ + *post_data = *next; + post_data++; + next++; + cn->post_read++; + if (cn->post_read == cn->content_length) + { + /* No more POST data to be copied */ + *post_data = '\0'; + return 1; + } + } + + /* More POST data has to be read. read_post_data will continue with that */ + cn->post_state = 1; + return 0; +} + +void read_post_data(struct connstruct *cn) +{ + char buf[MAXREQUESTLENGTH*4], *next; + char *post_data; + int rv; + + bzero(buf,MAXREQUESTLENGTH*4); + rv = special_read(cn, buf, sizeof(buf)-1); + if (rv <= 0) + { + if (rv < 0) /* really dead? */ + removeconnection(cn); + return; + } + + buf[rv] = '\0'; + next = buf; + + post_data = &cn->post_data[cn->post_read]; + + while (next < &buf[rv]) + { + *post_data = *next; + post_data++; + next++; + cn->post_read++; + if (cn->post_read == cn->content_length) + { + /* No more POST data to be copied */ + *post_data='\0'; + cn->post_state = 0; + buildactualfile(cn); + cn->state = STATE_WANT_TO_SEND_HEAD; + return; + } + } + + /* More POST data to read */ +} + +#endif /* CONFIG_HTTP_HAS_CGI */ + +/* Decode string %xx -> char (in place) */ +static void urldecode(char *buf) +{ + int v; + char *p, *s, *w; + + w = p = buf; + + while (*p) + { + v = 0; + + if (*p == '%') + { + s = p; + s++; + + if (isxdigit((int) s[0]) && isxdigit((int) s[1])) + { + v = hexit(s[0])*16 + hexit(s[1]); + + if (v) + { + /* do not decode %00 to null char */ + *w = (char)v; + p = &s[1]; + } + } + + } + + if (!v) *w=*p; + p++; + w++; + } + + *w='\0'; +} + +static int hexit(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + return 0; +} + +static void buildactualfile(struct connstruct *cn) +{ + char *cp; + snprintf(cn->actualfile, MAXREQUESTLENGTH, ".%s", cn->filereq); + +#ifndef WIN32 + /* Add directory slash if not there */ + if (isdir(cn->actualfile) && + cn->actualfile[strlen(cn->actualfile)-1] != '/') + strcat(cn->actualfile, "/"); + + /* work out the directory name */ + strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH); + if ((cp = strrchr(cn->dirname, '/')) == NULL) + cn->dirname[0] = 0; + else + *cp = 0; +#else + { + char curr_dir[MAXREQUESTLENGTH]; + char path[MAXREQUESTLENGTH]; + char *t = cn->actualfile; + + GetCurrentDirectory(MAXREQUESTLENGTH, curr_dir); + + /* convert all the forward slashes to back slashes */ + while ((t = strchr(t, '/'))) + *t++ = '\\'; + + snprintf(path, MAXREQUESTLENGTH, "%s%s", curr_dir, cn->actualfile); + memcpy(cn->actualfile, path, MAXREQUESTLENGTH); + + /* Add directory slash if not there */ + if (isdir(cn->actualfile) && + cn->actualfile[strlen(cn->actualfile)-1] != '\\') + strcat(cn->actualfile, "\\"); + + /* work out the directory name */ + strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH); + if ((cp = strrchr(cn->dirname, '\\')) == NULL) + cn->dirname[0] = 0; + else + *cp = 0; + } +#endif + +#if defined(CONFIG_HTTP_ENABLE_LUA) + /* + * Use the lua launcher if this file has a lua extension. Put this at the + * end as we need the directory name. + */ + if (cn->is_lua) + sprintf(cn->actualfile, "%s%s", CONFIG_HTTP_LUA_PREFIX, + CONFIG_HTTP_LUA_CGI_LAUNCHER); +#endif +} + +static int sanitizefile(const char *buf) +{ + int len, i; + + /* Don't accept anything not starting with a / */ + if (*buf != '/') + return 0; + + len = strlen(buf); + for (i = 0; i < len; i++) + { + /* Check for "/." i.e. don't send files starting with a . */ + if (buf[i] == '/' && buf[i+1] == '.') + return 0; + } + + return 1; +} + +static int sanitizehost(char *buf) +{ + while (*buf != '\0') + { + /* Handle the port */ + if (*buf == ':') + { + *buf = '\0'; + return 1; + } + + /* Enforce some basic URL rules... */ + if ((isalnum(*buf) == 0 && *buf != '-' && *buf != '.') || + (*buf == '.' && *(buf+1) == '.') || + (*buf == '.' && *(buf+1) == '-') || + (*buf == '-' && *(buf+1) == '.')) + return 0; + + buf++; + } + + return 1; +} + +static FILE * exist_check(struct connstruct *cn, const char *check_file) +{ + char pathname[MAXREQUESTLENGTH]; + snprintf(pathname, MAXREQUESTLENGTH, "%s/%s", cn->dirname, check_file); + return fopen(pathname, "r"); +} + +#ifdef CONFIG_HTTP_HAS_AUTHORIZATION +static void send_authenticate(struct connstruct *cn, const char *realm) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), HTTP_VERSION" 401 Unauthorized\n" + "WWW-Authenticate: Basic\n" + "realm=\"%s\"\n", realm); + special_write(cn, buf, strlen(buf)); +} + +static int check_digest(char *salt, const char *msg_passwd) +{ + uint8_t b256_salt[MAXREQUESTLENGTH]; + uint8_t real_passwd[MD5_SIZE]; + int salt_size; + char *b64_passwd; + uint8_t md5_result[MD5_SIZE]; + MD5_CTX ctx; + + /* retrieve the salt */ + if ((b64_passwd = strchr(salt, '$')) == NULL) + return -1; + + *b64_passwd++ = 0; + if (base64_decode(salt, strlen(salt), b256_salt, &salt_size)) + return -1; + + if (base64_decode(b64_passwd, strlen(b64_passwd), real_passwd, NULL)) + return -1; + + /* very simple MD5 crypt algorithm, but then the salt we use is large */ + MD5_Init(&ctx); + MD5_Update(&ctx, b256_salt, salt_size); /* process the salt */ + MD5_Update(&ctx, (uint8_t *)msg_passwd, strlen(msg_passwd)); + MD5_Final(md5_result, &ctx); + return memcmp(md5_result, real_passwd, MD5_SIZE);/* 0 = ok */ +} + +static int auth_check(struct connstruct *cn) +{ + char line[MAXREQUESTLENGTH]; + FILE *fp; + char *cp; + + if ((fp = exist_check(cn, ".htpasswd")) == NULL) + return 0; /* no .htpasswd file, so let though */ + + if (cn->authorization[0] == 0) + goto error; + + /* cn->authorization is in form "username:password" */ + if ((cp = strchr(cn->authorization, ':')) == NULL) + goto error; + else + *cp++ = 0; /* cp becomes the password */ + + while (fgets(line, sizeof(line), fp) != NULL) + { + char *b64_file_passwd; + int l = strlen(line); + + /* nuke newline */ + if (line[l-1] == '\n') + line[l-1] = 0; + + /* line is form "username:salt(b64)$password(b64)" */ + if ((b64_file_passwd = strchr(line, ':')) == NULL) + continue; + + *b64_file_passwd++ = 0; + + if (strcmp(line, cn->authorization)) /* our user? */ + continue; + + if (check_digest(b64_file_passwd, cp) == 0) + { + fclose(fp); + return 0; + } + } + +error: + fclose(fp); + send_authenticate(cn, cn->server_name); + return -1; +} +#endif + +static int htaccess_check(struct connstruct *cn) +{ + char line[MAXREQUESTLENGTH]; + FILE *fp; + int ret = 0; + + if ((fp = exist_check(cn, ".htaccess")) == NULL) + return 0; /* no .htaccess file, so let though */ + + while (fgets(line, sizeof(line), fp) != NULL) + { + if (strstr(line, "Deny all") || /* access to this dir denied */ + /* Access will be denied unless SSL is active */ + (!cn->is_ssl && strstr(line, "SSLRequireSSL")) || + /* Access will be denied if SSL is active */ + (cn->is_ssl && strstr(line, "SSLDenySSL"))) + { + ret = -1; + break; + } + } + + fclose(fp); + return ret; +} + +static void send_error(struct connstruct *cn, int err) +{ + char buf[MAXREQUESTLENGTH]; + char *title; + char *text; + + switch (err) + { + case 403: + title = "Forbidden"; + text = "File is protected"; +#ifdef CONFIG_HTTP_VERBOSE + printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH(); +#endif + break; + + case 404: + title = "Not Found"; + text = title; + break; + + case 418: + title = "POST data size is to large"; + text = title; + break; + + default: + title = "Unknown"; + text = "Unknown"; + break; + } + + snprintf(buf, MAXREQUESTLENGTH, "HTTP/1.1 %d %s\n" + "Content-Type: text/html\n" + "Cache-Control: no-cache,no-store\n" + "Connection: close\n\n" + "\n\n%d %s\n" + "

%d %s

\n\n", + err, title, err, title, err, text); + special_write(cn, buf, strlen(buf)); + removeconnection(cn); +} + +static const char *getmimetype(const char *name) +{ + /* only bother with a few mime types - let the browser figure the rest out */ + if (strstr(name, ".htm")) + return "text/html"; + else if (strstr(name, ".css")) + return "text/css"; + else + return "application/octet-stream"; +} + +static int special_write(struct connstruct *cn, + const char *buf, size_t count) +{ + if (cn->is_ssl) + { + SSL *ssl = cn->ssl; + return ssl ? ssl_write(ssl, (uint8_t *)buf, count) : -1; + } + else + return SOCKET_WRITE(cn->networkdesc, buf, count); +} + +static int special_read(struct connstruct *cn, void *buf, size_t count) +{ + int res; + + if (cn->is_ssl) + { + uint8_t *read_buf; + if ((res = ssl_read(cn->ssl, &read_buf)) > SSL_OK) + { + memcpy(buf, read_buf, res > (int)count ? count : res); + } + } + else + res = SOCKET_READ(cn->networkdesc, buf, count); + + return res; +} + diff --git a/libs/nixio/axTLS/httpd/tdate_parse.c b/libs/nixio/axTLS/httpd/tdate_parse.c new file mode 100644 index 000000000..813bdc578 --- /dev/null +++ b/libs/nixio/axTLS/httpd/tdate_parse.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include +#include +#include +#include +#include +#include +#include "axhttp.h" + +struct day_mon_map +{ + const char* s; + uint8_t l; +}; + +static struct day_mon_map wday_tab[] = +{ + { "Sun", 0 }, { "Mon", 1 }, { "Tue", 2 }, { "Wed", 3 }, + { "Thu", 4 }, { "Fri", 5 }, { "Sat", 6 }, +}; + +static struct day_mon_map mon_tab[] = +{ + { "Jan", 0 }, { "Feb", 1 }, { "Mar", 2 }, { "Apr", 3 }, + { "May", 4 }, { "Jun", 5 }, { "Jul", 6 }, { "Aug", 7 }, + { "Sep", 8 }, { "Oct", 9 }, { "Nov", 10 }, { "Dec", 11 }, +}; + +static int day_mon_map_compare(const char *v1, const char *v2) +{ + return strcmp(((struct day_mon_map*)v1)->s, ((struct day_mon_map*)v2)->s); +} + +void tdate_init(void) +{ + qsort(wday_tab, sizeof(wday_tab)/sizeof(struct day_mon_map), + sizeof(struct day_mon_map), + (int (*)(const void *, const void *))day_mon_map_compare); + qsort(mon_tab, sizeof(mon_tab)/sizeof(struct day_mon_map), + sizeof(struct day_mon_map), + (int (*)(const void *, const void *))day_mon_map_compare); +} + +static int8_t day_mon_map_search(const char* str, + const struct day_mon_map* tab, int n) +{ + struct day_mon_map *search = bsearch(&str, tab, n, + sizeof(struct day_mon_map), + (int (*)(const void *, const void *))day_mon_map_compare); + return search ? search->l : -1; +} + +time_t tdate_parse(const char* str) +{ + struct tm tm; + char str_mon[4], str_wday[4]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + + /* Initialize. */ + memset(&tm, 0, sizeof(struct tm)); + + /* wdy, DD mth YY HH:MM:SS GMT */ + if ((sscanf(str, "%3[a-zA-Z], %d %3[a-zA-Z] %d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 7) || + /* wdy mth DD HH:MM:SS YY */ + (sscanf(str, "%3[a-zA-Z] %3[a-zA-Z] %d %d:%d:%d %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year) == 7)) + { + int8_t tm_wday = day_mon_map_search(str_wday, wday_tab, + sizeof(wday_tab)/sizeof(struct day_mon_map)); + int8_t tm_mon = day_mon_map_search(str_mon, mon_tab, + sizeof(mon_tab)/sizeof(struct day_mon_map)); + + if (tm_wday < 0 || tm_mon < 0) + return -1; + + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year - 1900; + return mktime(&tm); + } + + return -1; /* error */ +} diff --git a/libs/nixio/axTLS/samples/Config.in b/libs/nixio/axTLS/samples/Config.in new file mode 100644 index 000000000..ecad25eeb --- /dev/null +++ b/libs/nixio/axTLS/samples/Config.in @@ -0,0 +1,63 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/config/Kconfig-language.txt +# +menu "Samples" + +config CONFIG_SAMPLES + bool "Create Samples" + default y + help + axTLS contains various sample code. + + Select Y here if you want to build the various samples. + +config CONFIG_C_SAMPLES + bool "axssl - C version" + default y + depends on CONFIG_SAMPLES + help + Build the "C" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). + +config CONFIG_CSHARP_SAMPLES + bool "axssl - C# version" + default y + depends on CONFIG_SAMPLES && CONFIG_CSHARP_BINDINGS + help + Build the "C#" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). + +config CONFIG_VBNET_SAMPLES + bool "axssl - VB.NET version" + default y + depends on CONFIG_SAMPLES && CONFIG_VBNET_BINDINGS + help + Build the "VB.NET" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). + +config CONFIG_JAVA_SAMPLES + bool "axssl - Java version" + default y + depends on CONFIG_SAMPLES && CONFIG_JAVA_BINDINGS + help + Build the "Java" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). + +config CONFIG_PERL_SAMPLES + bool "axssl - Perl version" + default y + depends on CONFIG_SAMPLES && CONFIG_PERL_BINDINGS + help + Build the "Perl" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). + +config CONFIG_LUA_SAMPLES + bool "axssl - Lua version" + default y + depends on CONFIG_SAMPLES && CONFIG_LUA_BINDINGS + help + Build the "Lua" version of axssl. The features enabled are very + dependent on the build mode ('full' mode will give all features). +endmenu + diff --git a/libs/nixio/axTLS/samples/Makefile b/libs/nixio/axTLS/samples/Makefile new file mode 100644 index 000000000..afbdd43d1 --- /dev/null +++ b/libs/nixio/axTLS/samples/Makefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +all: + +include ../config/.config +include ../config/makefile.conf + +all: +ifdef CONFIG_C_SAMPLES + $(MAKE) -C c +endif +ifdef CONFIG_CSHARP_SAMPLES + $(MAKE) -C csharp +endif +ifdef CONFIG_VBNET_SAMPLES + $(MAKE) -C vbnet +endif +ifdef CONFIG_JAVA_SAMPLES + $(MAKE) -C java +endif +ifdef CONFIG_PERL_SAMPLES + $(MAKE) -C perl +endif +ifdef CONFIG_LUA_SAMPLES + $(MAKE) -C lua +endif + +clean:: + $(MAKE) -C c clean + $(MAKE) -C csharp clean + $(MAKE) -C vbnet clean + $(MAKE) -C java clean + $(MAKE) -C perl clean + $(MAKE) -C lua clean diff --git a/libs/nixio/axTLS/samples/c/Makefile b/libs/nixio/axTLS/samples/c/Makefile new file mode 100644 index 000000000..17cf9e7c1 --- /dev/null +++ b/libs/nixio/axTLS/samples/c/Makefile @@ -0,0 +1,76 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +all : sample + +AXTLS_HOME=../.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +ifndef CONFIG_PLATFORM_WIN32 + +ifdef CONFIG_PLATFORM_CYGWIN +TARGET=$(AXTLS_HOME)/$(STAGE)/axssl.exe +else +TARGET=$(AXTLS_HOME)/$(STAGE)/axssl +endif # cygwin + +LIBS=$(AXTLS_HOME)/$(STAGE) +else +TARGET=$(AXTLS_HOME)/$(STAGE)/axssl.exe +endif + +ifndef CONFIG_C_SAMPLES +sample: + +else +sample : $(TARGET) +OBJ= axssl.o +include $(AXTLS_HOME)/config/makefile.post + +ifndef CONFIG_PLATFORM_WIN32 + +$(TARGET): $(OBJ) $(LIBS)/libaxtls.a + $(LD) $(LDFLAGS) -o $@ $(OBJ) -L$(LIBS) -laxtls +ifdef CONFIG_STRIP_UNWANTED_SECTIONS + $(STRIP) --remove-section=.comment $(TARGET) +endif # use strip +else # Win32 + +$(TARGET): $(OBJ) + $(LD) $(LDFLAGS) $(AXTLS_HOME)/config/axtls.res /out:$@ $^ /libpath:"$(AXTLS_HOME)/$(STAGE)" axtls.lib +endif + +endif # CONFIG_C_SAMPLES + +clean:: + -@rm -f $(AXTLS_HOME)/$(STAGE)/axssl* + diff --git a/libs/nixio/axTLS/samples/c/axssl.c b/libs/nixio/axTLS/samples/c/axssl.c new file mode 100644 index 000000000..6892ee452 --- /dev/null +++ b/libs/nixio/axTLS/samples/c/axssl.c @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Demonstrate the use of the axTLS library in C with a set of + * command-line parameters similar to openssl. In fact, openssl clients + * should be able to communicate with axTLS servers and visa-versa. + * + * This code has various bits enabled depending on the configuration. To enable + * the most interesting version, compile with the 'full mode' enabled. + * + * To see what options you have, run the following: + * > axssl s_server -? + * > axssl s_client -? + * + * The axtls shared library must be in the same directory or be found + * by the OS. + */ +#include +#include +#include +#include "ssl.h" + +/* define standard input */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +static void do_server(int argc, char *argv[]); +static void print_options(char *option); +static void print_server_options(char *option); +static void do_client(int argc, char *argv[]); +static void print_client_options(char *option); +static void display_cipher(SSL *ssl); +static void display_session_id(SSL *ssl); + +/** + * Main entry point. Doesn't do much except works out whether we are a client + * or a server. + */ +int main(int argc, char *argv[]) +{ +#ifdef WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + WSAStartup(wVersionRequested, &wsaData); +#elif !defined(CONFIG_PLATFORM_SOLARIS) + signal(SIGPIPE, SIG_IGN); /* ignore pipe errors */ +#endif + + if (argc == 2 && strcmp(argv[1], "version") == 0) + { + printf("axssl %s %s\n", ssl_version(), __DATE__); + exit(0); + } + + if (argc < 2 || ( + strcmp(argv[1], "s_server") && strcmp(argv[1], "s_client"))) + print_options(argc > 1 ? argv[1] : ""); + + strcmp(argv[1], "s_server") ? + do_client(argc, argv) : do_server(argc, argv); + return 0; +} + +/** + * Implement the SSL server logic. + */ +static void do_server(int argc, char *argv[]) +{ + int i = 2; + uint16_t port = 4433; + uint32_t options = SSL_DISPLAY_CERTS; + int client_fd; + SSL_CTX *ssl_ctx; + int server_fd, res = 0; + socklen_t client_len; +#ifndef CONFIG_SSL_SKELETON_MODE + char *private_key_file = NULL; + const char *password = NULL; + char **cert; + int cert_index = 0; + int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); +#endif +#ifdef WIN32 + char yes = 1; +#else + int yes = 1; +#endif + struct sockaddr_in serv_addr; + struct sockaddr_in client_addr; + int quiet = 0; +#ifdef CONFIG_SSL_CERT_VERIFICATION + int ca_cert_index = 0; + int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); + char **ca_cert = (char **)calloc(1, sizeof(char *)*ca_cert_size); +#endif + fd_set read_set; + +#ifndef CONFIG_SSL_SKELETON_MODE + cert = (char **)calloc(1, sizeof(char *)*cert_size); +#endif + + while (i < argc) + { + if (strcmp(argv[i], "-accept") == 0) + { + if (i >= argc-1) + { + print_server_options(argv[i]); + } + + port = atoi(argv[++i]); + } +#ifndef CONFIG_SSL_SKELETON_MODE + else if (strcmp(argv[i], "-cert") == 0) + { + if (i >= argc-1 || cert_index >= cert_size) + { + print_server_options(argv[i]); + } + + cert[cert_index++] = argv[++i]; + } + else if (strcmp(argv[i], "-key") == 0) + { + if (i >= argc-1) + { + print_server_options(argv[i]); + } + + private_key_file = argv[++i]; + options |= SSL_NO_DEFAULT_KEY; + } + else if (strcmp(argv[i], "-pass") == 0) + { + if (i >= argc-1) + { + print_server_options(argv[i]); + } + + password = argv[++i]; + } +#endif + else if (strcmp(argv[i], "-quiet") == 0) + { + quiet = 1; + options &= ~SSL_DISPLAY_CERTS; + } +#ifdef CONFIG_SSL_CERT_VERIFICATION + else if (strcmp(argv[i], "-verify") == 0) + { + options |= SSL_CLIENT_AUTHENTICATION; + } + else if (strcmp(argv[i], "-CAfile") == 0) + { + if (i >= argc-1 || ca_cert_index >= ca_cert_size) + { + print_server_options(argv[i]); + } + + ca_cert[ca_cert_index++] = argv[++i]; + } +#endif +#ifdef CONFIG_SSL_FULL_MODE + else if (strcmp(argv[i], "-debug") == 0) + { + options |= SSL_DISPLAY_BYTES; + } + else if (strcmp(argv[i], "-state") == 0) + { + options |= SSL_DISPLAY_STATES; + } + else if (strcmp(argv[i], "-show-rsa") == 0) + { + options |= SSL_DISPLAY_RSA; + } +#endif + else /* don't know what this is */ + { + print_server_options(argv[i]); + } + + i++; + } + + if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_SVR_SESS)) == NULL) + { + fprintf(stderr, "Error: Server context is invalid\n"); + exit(1); + } + +#ifndef CONFIG_SSL_SKELETON_MODE + if (private_key_file) + { + int obj_type = SSL_OBJ_RSA_KEY; + + /* auto-detect the key type from the file extension */ + if (strstr(private_key_file, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key_file, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)) + { + fprintf(stderr, "Error: Private key '%s' is undefined.\n", + private_key_file); + exit(1); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert[i], NULL)) + { + printf("Certificate '%s' is undefined.\n", cert[i]); + exit(1); + } + } +#endif + +#ifdef CONFIG_SSL_CERT_VERIFICATION + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_cert[i], NULL)) + { + printf("Certificate '%s' is undefined.\n", ca_cert[i]); + exit(1); + } + } + + free(ca_cert); +#endif +#ifndef CONFIG_SSL_SKELETON_MODE + free(cert); +#endif + + /* Create socket for incoming connections */ + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket"); + return; + } + + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + /* Construct local address structure */ + memset(&serv_addr, 0, sizeof(serv_addr)); /* Zero out structure */ + serv_addr.sin_family = AF_INET; /* Internet address family */ + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + serv_addr.sin_port = htons(port); /* Local port */ + + /* Bind to the local address */ + if (bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + perror("bind"); + exit(1); + } + + if (listen(server_fd, 5) < 0) + { + perror("listen"); + exit(1); + } + + client_len = sizeof(client_addr); + + /************************************************************************* + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + *************************************************************************/ + for (;;) + { + SSL *ssl; + int reconnected = 0; + + if (!quiet) + { + printf("ACCEPT\n"); + TTY_FLUSH(); + } + + if ((client_fd = accept(server_fd, + (struct sockaddr *)&client_addr, &client_len)) < 0) + { + res = 1; + break; + } + + ssl = ssl_server_new(ssl_ctx, client_fd); + + /* now read (and display) whatever the client sends us */ + for (;;) + { + /* allow parallel reading of client and standard input */ + FD_ZERO(&read_set); + FD_SET(client_fd, &read_set); + +#ifndef WIN32 + /* win32 doesn't like mixing up stdin and sockets */ + if (isatty(STDIN_FILENO))/* but only if we are in an active shell */ + { + FD_SET(STDIN_FILENO, &read_set); + } + + if ((res = select(client_fd+1, &read_set, NULL, NULL, NULL)) > 0) + { + uint8_t buf[1024]; + + /* read standard input? */ + if (FD_ISSET(STDIN_FILENO, &read_set)) + { + if (fgets((char *)buf, sizeof(buf), stdin) == NULL) + { + res = SSL_ERROR_CONN_LOST; + } + else + { + /* small hack to check renegotiation */ + if (buf[0] == 'r' && (buf[1] == '\n' || buf[1] == '\r')) + { + res = ssl_renegotiate(ssl); + } + else /* write our ramblings to the client */ + { + res = ssl_write(ssl, buf, strlen((char *)buf)+1); + } + } + } + else /* a socket read */ +#endif + { + /* keep reading until we get something interesting */ + uint8_t *read_buf; + + if ((res = ssl_read(ssl, &read_buf)) == SSL_OK) + { + /* are we in the middle of doing a handshake? */ + if (ssl_handshake_status(ssl) != SSL_OK) + { + reconnected = 0; + } + else if (!reconnected) + { + /* we are connected/reconnected */ + if (!quiet) + { + display_session_id(ssl); + display_cipher(ssl); + } + + reconnected = 1; + } + } + + if (res > SSL_OK) /* display our interesting output */ + { + printf("%s", read_buf); + TTY_FLUSH(); + } + else if (res < SSL_OK && !quiet) + { + ssl_display_error(res); + } + } +#ifndef WIN32 + } +#endif + + if (res < SSL_OK) + { + if (!quiet) + { + printf("CONNECTION CLOSED\n"); + TTY_FLUSH(); + } + + break; + } + } + + /* client was disconnected or the handshake failed. */ + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + } + + ssl_ctx_free(ssl_ctx); +} + +/** + * Implement the SSL client logic. + */ +static void do_client(int argc, char *argv[]) +{ +#ifdef CONFIG_SSL_ENABLE_CLIENT + int res, i = 2; + uint16_t port = 4433; + uint32_t options = SSL_SERVER_VERIFY_LATER|SSL_DISPLAY_CERTS; + int client_fd; + char *private_key_file = NULL; + struct sockaddr_in client_addr; + struct hostent *hostent; + int reconnect = 0; + uint32_t sin_addr; + SSL_CTX *ssl_ctx; + SSL *ssl = NULL; + int quiet = 0; + int cert_index = 0, ca_cert_index = 0; + int cert_size, ca_cert_size; + char **ca_cert, **cert; + uint8_t session_id[SSL_SESSION_ID_SIZE]; + fd_set read_set; + const char *password = NULL; + + FD_ZERO(&read_set); + sin_addr = inet_addr("127.0.0.1"); + cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); + ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); + ca_cert = (char **)calloc(1, sizeof(char *)*ca_cert_size); + cert = (char **)calloc(1, sizeof(char *)*cert_size); + + while (i < argc) + { + if (strcmp(argv[i], "-connect") == 0) + { + char *host, *ptr; + + if (i >= argc-1) + { + print_client_options(argv[i]); + } + + host = argv[++i]; + if ((ptr = strchr(host, ':')) == NULL) + { + print_client_options(argv[i]); + } + + *ptr++ = 0; + port = atoi(ptr); + hostent = gethostbyname(host); + + if (hostent == NULL) + { + print_client_options(argv[i]); + } + + sin_addr = *((uint32_t **)hostent->h_addr_list)[0]; + } + else if (strcmp(argv[i], "-cert") == 0) + { + if (i >= argc-1 || cert_index >= cert_size) + { + print_client_options(argv[i]); + } + + cert[cert_index++] = argv[++i]; + } + else if (strcmp(argv[i], "-key") == 0) + { + if (i >= argc-1) + { + print_client_options(argv[i]); + } + + private_key_file = argv[++i]; + options |= SSL_NO_DEFAULT_KEY; + } + else if (strcmp(argv[i], "-CAfile") == 0) + { + if (i >= argc-1 || ca_cert_index >= ca_cert_size) + { + print_client_options(argv[i]); + } + + ca_cert[ca_cert_index++] = argv[++i]; + } + else if (strcmp(argv[i], "-verify") == 0) + { + options &= ~SSL_SERVER_VERIFY_LATER; + } + else if (strcmp(argv[i], "-reconnect") == 0) + { + reconnect = 4; + } + else if (strcmp(argv[i], "-quiet") == 0) + { + quiet = 1; + options &= ~SSL_DISPLAY_CERTS; + } + else if (strcmp(argv[i], "-pass") == 0) + { + if (i >= argc-1) + { + print_client_options(argv[i]); + } + + password = argv[++i]; + } +#ifdef CONFIG_SSL_FULL_MODE + else if (strcmp(argv[i], "-debug") == 0) + { + options |= SSL_DISPLAY_BYTES; + } + else if (strcmp(argv[i], "-state") == 0) + { + options |= SSL_DISPLAY_STATES; + } + else if (strcmp(argv[i], "-show-rsa") == 0) + { + options |= SSL_DISPLAY_RSA; + } +#endif + else /* don't know what this is */ + { + print_client_options(argv[i]); + } + + i++; + } + + if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) + { + fprintf(stderr, "Error: Client context is invalid\n"); + exit(1); + } + + if (private_key_file) + { + int obj_type = SSL_OBJ_RSA_KEY; + + /* auto-detect the key type from the file extension */ + if (strstr(private_key_file, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key_file, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)) + { + fprintf(stderr, "Error: Private key '%s' is undefined.\n", + private_key_file); + exit(1); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert[i], NULL)) + { + printf("Certificate '%s' is undefined.\n", cert[i]); + exit(1); + } + } + + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_cert[i], NULL)) + { + printf("Certificate '%s' is undefined.\n", ca_cert[i]); + exit(1); + } + } + + free(cert); + free(ca_cert); + + /************************************************************************* + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + *************************************************************************/ + client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + memset(&client_addr, 0, sizeof(client_addr)); + client_addr.sin_family = AF_INET; + client_addr.sin_port = htons(port); + client_addr.sin_addr.s_addr = sin_addr; + + if (connect(client_fd, (struct sockaddr *)&client_addr, + sizeof(client_addr)) < 0) + { + perror("connect"); + exit(1); + } + + if (!quiet) + { + printf("CONNECTED\n"); + TTY_FLUSH(); + } + + /* Try session resumption? */ + if (reconnect) + { + while (reconnect--) + { + ssl = ssl_client_new(ssl_ctx, client_fd, session_id, + sizeof(session_id)); + if ((res = ssl_handshake_status(ssl)) != SSL_OK) + { + if (!quiet) + { + ssl_display_error(res); + } + + ssl_free(ssl); + exit(1); + } + + display_session_id(ssl); + memcpy(session_id, ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE); + + if (reconnect) + { + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + + client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + connect(client_fd, (struct sockaddr *)&client_addr, + sizeof(client_addr)); + } + } + } + else + { + ssl = ssl_client_new(ssl_ctx, client_fd, NULL, 0); + } + + /* check the return status */ + if ((res = ssl_handshake_status(ssl)) != SSL_OK) + { + if (!quiet) + { + ssl_display_error(res); + } + + exit(1); + } + + if (!quiet) + { + const char *common_name = ssl_get_cert_dn(ssl, + SSL_X509_CERT_COMMON_NAME); + if (common_name) + { + printf("Common Name:\t\t\t%s\n", common_name); + } + + display_session_id(ssl); + display_cipher(ssl); + } + + for (;;) + { + uint8_t buf[1024]; + res = SSL_OK; + + /* allow parallel reading of server and standard input */ + FD_SET(client_fd, &read_set); +#ifndef WIN32 + /* win32 doesn't like mixing up stdin and sockets */ + FD_SET(STDIN_FILENO, &read_set); + + if ((res = select(client_fd+1, &read_set, NULL, NULL, NULL)) > 0) + { + /* read standard input? */ + if (FD_ISSET(STDIN_FILENO, &read_set)) +#endif + { + if (fgets((char *)buf, sizeof(buf), stdin) == NULL) + { + /* bomb out of here */ + ssl_free(ssl); + break; + } + else + { + /* small hack to check renegotiation */ + if (buf[0] == 'R' && (buf[1] == '\n' || buf[1] == '\r')) + { + res = ssl_renegotiate(ssl); + } + else + { + res = ssl_write(ssl, buf, strlen((char *)buf)+1); + } + } + } +#ifndef WIN32 + else /* a socket read */ + { + uint8_t *read_buf; + + res = ssl_read(ssl, &read_buf); + + if (res > 0) /* display our interesting output */ + { + printf("%s", read_buf); + TTY_FLUSH(); + } + } + } +#endif + + if (res < 0) + { + if (!quiet) + { + ssl_display_error(res); + } + + break; /* get outta here */ + } + } + + ssl_ctx_free(ssl_ctx); + SOCKET_CLOSE(client_fd); +#else + print_client_options(argv[1]); +#endif +} + +/** + * We've had some sort of command-line error. Print out the basic options. + */ +static void print_options(char *option) +{ + printf("axssl: Error: '%s' is an invalid command.\n", option); + printf("usage: axssl [s_server|s_client|version] [args ...]\n"); + exit(1); +} + +/** + * We've had some sort of command-line error. Print out the server options. + */ +static void print_server_options(char *option) +{ +#ifndef CONFIG_SSL_SKELETON_MODE + int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); +#endif +#ifdef CONFIG_SSL_CERT_VERIFICATION + int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); +#endif + + printf("unknown option %s\n", option); + printf("usage: s_server [args ...]\n"); + printf(" -accept arg\t- port to accept on (default is 4433)\n"); +#ifndef CONFIG_SSL_SKELETON_MODE + printf(" -cert arg\t- certificate file to add (in addition to default)" + " to chain -\n" + "\t\t Can repeat up to %d times\n", cert_size); + printf(" -key arg\t- Private key file to use\n"); + printf(" -pass\t\t- private key file pass phrase source\n"); +#endif + printf(" -quiet\t\t- No server output\n"); +#ifdef CONFIG_SSL_CERT_VERIFICATION + printf(" -verify\t- turn on peer certificate verification\n"); + printf(" -CAfile arg\t- Certificate authority\n"); + printf("\t\t Can repeat up to %d times\n", ca_cert_size); +#endif +#ifdef CONFIG_SSL_FULL_MODE + printf(" -debug\t\t- Print more output\n"); + printf(" -state\t\t- Show state messages\n"); + printf(" -show-rsa\t- Show RSA state\n"); +#endif + exit(1); +} + +/** + * We've had some sort of command-line error. Print out the client options. + */ +static void print_client_options(char *option) +{ +#ifdef CONFIG_SSL_ENABLE_CLIENT + int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); + int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); +#endif + + printf("unknown option %s\n", option); +#ifdef CONFIG_SSL_ENABLE_CLIENT + printf("usage: s_client [args ...]\n"); + printf(" -connect host:port - who to connect to (default " + "is localhost:4433)\n"); + printf(" -verify\t- turn on peer certificate verification\n"); + printf(" -cert arg\t- certificate file to use\n"); + printf("\t\t Can repeat up to %d times\n", cert_size); + printf(" -key arg\t- Private key file to use\n"); + printf(" -CAfile arg\t- Certificate authority\n"); + printf("\t\t Can repeat up to %d times\n", ca_cert_size); + printf(" -quiet\t\t- No client output\n"); + printf(" -reconnect\t- Drop and re-make the connection " + "with the same Session-ID\n"); + printf(" -pass\t\t- private key file pass phrase source\n"); +#ifdef CONFIG_SSL_FULL_MODE + printf(" -debug\t\t- Print more output\n"); + printf(" -state\t\t- Show state messages\n"); + printf(" -show-rsa\t- Show RSA state\n"); +#endif +#else + printf("Change configuration to allow this feature\n"); +#endif + exit(1); +} + +/** + * Display what cipher we are using + */ +static void display_cipher(SSL *ssl) +{ + printf("CIPHER is "); + switch (ssl_get_cipher_id(ssl)) + { + case SSL_AES128_SHA: + printf("AES128-SHA"); + break; + + case SSL_AES256_SHA: + printf("AES256-SHA"); + break; + + case SSL_RC4_128_SHA: + printf("RC4-SHA"); + break; + + case SSL_RC4_128_MD5: + printf("RC4-MD5"); + break; + + default: + printf("Unknown - %d", ssl_get_cipher_id(ssl)); + break; + } + + printf("\n"); + TTY_FLUSH(); +} + +/** + * Display what session id we have. + */ +static void display_session_id(SSL *ssl) +{ + int i; + const uint8_t *session_id = ssl_get_session_id(ssl); + int sess_id_size = ssl_get_session_id_size(ssl); + + if (sess_id_size > 0) + { + printf("-----BEGIN SSL SESSION PARAMETERS-----\n"); + for (i = 0; i < sess_id_size; i++) + { + printf("%02x", session_id[i]); + } + + printf("\n-----END SSL SESSION PARAMETERS-----\n"); + TTY_FLUSH(); + } +} diff --git a/libs/nixio/axTLS/samples/csharp/Makefile b/libs/nixio/axTLS/samples/csharp/Makefile new file mode 100644 index 000000000..46c2421dc --- /dev/null +++ b/libs/nixio/axTLS/samples/csharp/Makefile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf +include ../../config/makefile.dotnet.conf + +all : sample +TARGET=../../$(STAGE)/axssl.csharp.exe +sample : $(TARGET) + +$(TARGET): ../../bindings/csharp/axTLS.cs ../../bindings/csharp/axInterface.cs axssl.cs +ifdef GO_DOT_NET + csc.exe /nologo /t:exe /out:"`cygpath -w $@`" $(foreach file, $^, "`cygpath -w $(file)`") +else # use mono to build + mcs -out:$@ $^ + +endif # ARCH + +clean:: + -@rm -f $(TARGET) diff --git a/libs/nixio/axTLS/samples/csharp/axssl.cs b/libs/nixio/axTLS/samples/csharp/axssl.cs new file mode 100644 index 000000000..dae2b8a41 --- /dev/null +++ b/libs/nixio/axTLS/samples/csharp/axssl.cs @@ -0,0 +1,758 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Demonstrate the use of the axTLS library in C# with a set of + * command-line parameters similar to openssl. In fact, openssl clients + * should be able to communicate with axTLS servers and visa-versa. + * + * This code has various bits enabled depending on the configuration. To enable + * the most interesting version, compile with the 'full mode' enabled. + * + * To see what options you have, run the following: + * > axssl.csharp.exe s_server -? + * > axssl.csharp.exe s_client -? + * + * The axtls shared library must be in the same directory or be found + * by the OS. + */ + +using System; +using System.Net; +using System.Net.Sockets; +using axTLS; + +public class axssl +{ + /* + * Main() + */ + public static void Main(string[] args) + { + if (args.Length == 1 && args[0] == "version") + { + Console.WriteLine("axssl.csharp " + SSLUtil.Version()); + Environment.Exit(0); + } + + axssl runner = new axssl(); + + if (args.Length < 1 || (args[0] != "s_server" && args[0] != "s_client")) + runner.print_options(args.Length > 0 ? args[0] : ""); + + int build_mode = SSLUtil.BuildMode(); + + if (args[0] == "s_server") + runner.do_server(build_mode, args); + else + runner.do_client(build_mode, args); + } + + /* + * do_server() + */ + private void do_server(int build_mode, string[] args) + { + int i = 1; + int port = 4433; + uint options = axtls.SSL_DISPLAY_CERTS; + bool quiet = false; + string password = null; + string private_key_file = null; + + /* organise the cert/ca_cert lists */ + int cert_size = SSLUtil.MaxCerts(); + int ca_cert_size = SSLUtil.MaxCACerts(); + string[] cert = new string[cert_size]; + string[] ca_cert = new string[ca_cert_size]; + int cert_index = 0; + int ca_cert_index = 0; + + while (i < args.Length) + { + if (args[i] == "-accept") + { + if (i >= args.Length-1) + { + print_server_options(build_mode, args[i]); + } + + port = Int32.Parse(args[++i]); + } + else if (args[i] == "-quiet") + { + quiet = true; + options &= ~(uint)axtls.SSL_DISPLAY_CERTS; + } + else if (build_mode >= axtls.SSL_BUILD_SERVER_ONLY) + { + if (args[i] == "-cert") + { + if (i >= args.Length-1 || cert_index >= cert_size) + { + print_server_options(build_mode, args[i]); + } + + cert[cert_index++] = args[++i]; + } + else if (args[i] == "-key") + { + if (i >= args.Length-1) + { + print_server_options(build_mode, args[i]); + } + + private_key_file = args[++i]; + options |= axtls.SSL_NO_DEFAULT_KEY; + } + else if (args[i] == "-pass") + { + if (i >= args.Length-1) + { + print_server_options(build_mode, args[i]); + } + + password = args[++i]; + } + else if (build_mode >= axtls.SSL_BUILD_ENABLE_VERIFICATION) + { + if (args[i] == "-verify") + { + options |= axtls.SSL_CLIENT_AUTHENTICATION; + } + else if (args[i] == "-CAfile") + { + if (i >= args.Length-1 || ca_cert_index >= ca_cert_size) + { + print_server_options(build_mode, args[i]); + } + + ca_cert[ca_cert_index++] = args[++i]; + } + else if (build_mode == axtls.SSL_BUILD_FULL_MODE) + { + if (args[i] == "-debug") + { + options |= axtls.SSL_DISPLAY_BYTES; + } + else if (args[i] == "-state") + { + options |= axtls.SSL_DISPLAY_STATES; + } + else if (args[i] == "-show-rsa") + { + options |= axtls.SSL_DISPLAY_RSA; + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + + i++; + } + + /* Create socket for incoming connections */ + IPEndPoint ep = new IPEndPoint(IPAddress.Any, port); + TcpListener server_sock = new TcpListener(ep); + server_sock.Start(); + + /********************************************************************** + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + **********************************************************************/ + SSLServer ssl_ctx = new SSLServer( + options, axtls.SSL_DEFAULT_SVR_SESS); + + if (ssl_ctx == null) + { + Console.Error.WriteLine("Error: Server context is invalid"); + Environment.Exit(1); + } + + if (private_key_file != null) + { + int obj_type = axtls.SSL_OBJ_RSA_KEY; + + if (private_key_file.EndsWith(".p8")) + obj_type = axtls.SSL_OBJ_PKCS8; + else if (private_key_file.EndsWith(".p12")) + obj_type = axtls.SSL_OBJ_PKCS12; + + if (ssl_ctx.ObjLoad(obj_type, + private_key_file, password) != axtls.SSL_OK) + { + Console.Error.WriteLine("Private key '" + private_key_file + + "' is undefined."); + Environment.Exit(1); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CERT, + cert[i], null) != axtls.SSL_OK) + { + Console.WriteLine("Certificate '" + cert[i] + + "' is undefined."); + Environment.Exit(1); + } + } + + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CACERT, + ca_cert[i], null) != axtls.SSL_OK) + { + Console.WriteLine("Certificate '" + cert[i] + + "' is undefined."); + Environment.Exit(1); + } + } + + byte[] buf = null; + int res; + + for (;;) + { + if (!quiet) + { + Console.WriteLine("ACCEPT"); + } + + Socket client_sock = server_sock.AcceptSocket(); + + SSL ssl = ssl_ctx.Connect(client_sock); + + /* do the actual SSL handshake */ + while ((res = ssl_ctx.Read(ssl, out buf)) == axtls.SSL_OK) + { + /* check when the connection has been established */ + if (ssl.HandshakeStatus() == axtls.SSL_OK) + break; + + /* could do something else here */ + } + + if (res == axtls.SSL_OK) /* connection established and ok */ + { + if (!quiet) + { + display_session_id(ssl); + display_cipher(ssl); + } + + /* now read (and display) whatever the client sends us */ + for (;;) + { + /* keep reading until we get something interesting */ + while ((res = ssl_ctx.Read(ssl, out buf)) == axtls.SSL_OK) + { + /* could do something else here */ + } + + if (res < axtls.SSL_OK) + { + if (!quiet) + { + Console.WriteLine("CONNECTION CLOSED"); + } + + break; + } + + /* convert to string */ + char[] str = new char[res]; + for (i = 0; i < res; i++) + { + str[i] = (char)buf[i]; + } + + Console.Write(str); + } + } + else if (!quiet) + { + SSLUtil.DisplayError(res); + } + + /* client was disconnected or the handshake failed. */ + ssl.Dispose(); + client_sock.Close(); + } + + /* ssl_ctx.Dispose(); */ + } + + /* + * do_client() + */ + private void do_client(int build_mode, string[] args) + { + if (build_mode < axtls.SSL_BUILD_ENABLE_CLIENT) + { + print_client_options(build_mode, args[1]); + } + + int i = 1, res; + int port = 4433; + bool quiet = false; + string password = null; + int reconnect = 0; + string private_key_file = null; + string hostname = "127.0.0.1"; + + /* organise the cert/ca_cert lists */ + int cert_index = 0; + int ca_cert_index = 0; + int cert_size = SSLUtil.MaxCerts(); + int ca_cert_size = SSLUtil.MaxCACerts(); + string[] cert = new string[cert_size]; + string[] ca_cert = new string[ca_cert_size]; + + uint options = axtls.SSL_SERVER_VERIFY_LATER|axtls.SSL_DISPLAY_CERTS; + byte[] session_id = null; + + while (i < args.Length) + { + if (args[i] == "-connect") + { + string host_port; + + if (i >= args.Length-1) + { + print_client_options(build_mode, args[i]); + } + + host_port = args[++i]; + int index_colon; + + if ((index_colon = host_port.IndexOf(':')) < 0) + print_client_options(build_mode, args[i]); + + hostname = new string(host_port.ToCharArray(), + 0, index_colon); + port = Int32.Parse(new String(host_port.ToCharArray(), + index_colon+1, host_port.Length-index_colon-1)); + } + else if (args[i] == "-cert") + { + if (i >= args.Length-1 || cert_index >= cert_size) + { + print_client_options(build_mode, args[i]); + } + + cert[cert_index++] = args[++i]; + } + else if (args[i] == "-key") + { + if (i >= args.Length-1) + { + print_client_options(build_mode, args[i]); + } + + private_key_file = args[++i]; + options |= axtls.SSL_NO_DEFAULT_KEY; + } + else if (args[i] == "-CAfile") + { + if (i >= args.Length-1 || ca_cert_index >= ca_cert_size) + { + print_client_options(build_mode, args[i]); + } + + ca_cert[ca_cert_index++] = args[++i]; + } + else if (args[i] == "-verify") + { + options &= ~(uint)axtls.SSL_SERVER_VERIFY_LATER; + } + else if (args[i] == "-reconnect") + { + reconnect = 4; + } + else if (args[i] == "-quiet") + { + quiet = true; + options &= ~(uint)axtls.SSL_DISPLAY_CERTS; + } + else if (args[i] == "-pass") + { + if (i >= args.Length-1) + { + print_client_options(build_mode, args[i]); + } + + password = args[++i]; + } + else if (build_mode == axtls.SSL_BUILD_FULL_MODE) + { + if (args[i] == "-debug") + { + options |= axtls.SSL_DISPLAY_BYTES; + } + else if (args[i] == "-state") + { + options |= axtls.SSL_DISPLAY_STATES; + } + else if (args[i] == "-show-rsa") + { + options |= axtls.SSL_DISPLAY_RSA; + } + else + print_client_options(build_mode, args[i]); + } + else /* don't know what this is */ + print_client_options(build_mode, args[i]); + + i++; + } + + // IPHostEntry hostInfo = Dns.Resolve(hostname); + IPHostEntry hostInfo = Dns.GetHostEntry(hostname); + IPAddress[] addresses = hostInfo.AddressList; + IPEndPoint ep = new IPEndPoint(addresses[0], port); + Socket client_sock = new Socket(AddressFamily.InterNetwork, + SocketType.Stream, ProtocolType.Tcp); + client_sock.Connect(ep); + + if (!client_sock.Connected) + { + Console.WriteLine("could not connect"); + Environment.Exit(1); + } + + if (!quiet) + { + Console.WriteLine("CONNECTED"); + } + + /********************************************************************** + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + **********************************************************************/ + SSLClient ssl_ctx = new SSLClient(options, + axtls.SSL_DEFAULT_CLNT_SESS); + + if (ssl_ctx == null) + { + Console.Error.WriteLine("Error: Client context is invalid"); + Environment.Exit(1); + } + + if (private_key_file != null) + { + int obj_type = axtls.SSL_OBJ_RSA_KEY; + + if (private_key_file.EndsWith(".p8")) + obj_type = axtls.SSL_OBJ_PKCS8; + else if (private_key_file.EndsWith(".p12")) + obj_type = axtls.SSL_OBJ_PKCS12; + + if (ssl_ctx.ObjLoad(obj_type, + private_key_file, password) != axtls.SSL_OK) + { + Console.Error.WriteLine("Private key '" + private_key_file + + "' is undefined."); + Environment.Exit(1); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CERT, + cert[i], null) != axtls.SSL_OK) + { + Console.WriteLine("Certificate '" + cert[i] + + "' is undefined."); + Environment.Exit(1); + } + } + + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CACERT, + ca_cert[i], null) != axtls.SSL_OK) + { + Console.WriteLine("Certificate '" + cert[i] + + "' is undefined."); + Environment.Exit(1); + } + } + + SSL ssl = new SSL(new IntPtr(0)); /* keep compiler happy */ + + /* Try session resumption? */ + if (reconnect > 0) + { + while (reconnect-- > 0) + { + ssl = ssl_ctx.Connect(client_sock, session_id); + + if ((res = ssl.HandshakeStatus()) != axtls.SSL_OK) + { + if (!quiet) + { + SSLUtil.DisplayError(res); + } + + ssl.Dispose(); + Environment.Exit(1); + } + + display_session_id(ssl); + session_id = ssl.GetSessionId(); + + if (reconnect > 0) + { + ssl.Dispose(); + client_sock.Close(); + + /* and reconnect */ + client_sock = new Socket(AddressFamily.InterNetwork, + SocketType.Stream, ProtocolType.Tcp); + client_sock.Connect(ep); + } + } + } + else + { + ssl = ssl_ctx.Connect(client_sock, null); + } + + /* check the return status */ + if ((res = ssl.HandshakeStatus()) != axtls.SSL_OK) + { + if (!quiet) + { + SSLUtil.DisplayError(res); + } + + Environment.Exit(1); + } + + if (!quiet) + { + string common_name = + ssl.GetCertificateDN(axtls.SSL_X509_CERT_COMMON_NAME); + + if (common_name != null) + { + Console.WriteLine("Common Name:\t\t\t" + common_name); + } + + display_session_id(ssl); + display_cipher(ssl); + } + + for (;;) + { + string user_input = Console.ReadLine(); + + if (user_input == null) + break; + + byte[] buf = new byte[user_input.Length+2]; + buf[buf.Length-2] = (byte)'\n'; /* add the carriage return */ + buf[buf.Length-1] = 0; /* null terminate */ + + for (i = 0; i < buf.Length-2; i++) + { + buf[i] = (byte)user_input[i]; + } + + if ((res = ssl_ctx.Write(ssl, buf, buf.Length)) < axtls.SSL_OK) + { + if (!quiet) + { + SSLUtil.DisplayError(res); + } + + break; + } + } + + ssl_ctx.Dispose(); + } + + /** + * We've had some sort of command-line error. Print out the basic options. + */ + private void print_options(string option) + { + Console.WriteLine("axssl: Error: '" + option + + "' is an invalid command."); + Console.WriteLine("usage: axssl.csharp [s_server|" + + "s_client|version] [args ...]"); + Environment.Exit(1); + } + + /** + * We've had some sort of command-line error. Print out the server options. + */ + private void print_server_options(int build_mode, string option) + { + int cert_size = SSLUtil.MaxCerts(); + int ca_cert_size = SSLUtil.MaxCACerts(); + + Console.WriteLine("unknown option " + option); + Console.WriteLine("usage: s_server [args ...]"); + Console.WriteLine(" -accept arg\t- port to accept on (default " + + "is 4433)"); + Console.WriteLine(" -quiet\t\t- No server output"); + + if (build_mode >= axtls.SSL_BUILD_SERVER_ONLY) + { + Console.WriteLine(" -cert arg\t- certificate file to add (in " + + "addition to default) to chain -"); + Console.WriteLine("\t\t Can repeat up to " + cert_size + " times"); + Console.WriteLine(" -key arg\t- Private key file to use"); + Console.WriteLine(" -pass\t\t- private key file pass phrase source"); + } + + if (build_mode >= axtls.SSL_BUILD_ENABLE_VERIFICATION) + { + Console.WriteLine(" -verify\t- turn on peer certificate " + + "verification"); + Console.WriteLine(" -CAfile arg\t- Certificate authority."); + Console.WriteLine("\t\t Can repeat up to " + + ca_cert_size + "times"); + } + + if (build_mode == axtls.SSL_BUILD_FULL_MODE) + { + Console.WriteLine(" -debug\t\t- Print more output"); + Console.WriteLine(" -state\t\t- Show state messages"); + Console.WriteLine(" -show-rsa\t- Show RSA state"); + } + + Environment.Exit(1); + } + + /** + * We've had some sort of command-line error. Print out the client options. + */ + private void print_client_options(int build_mode, string option) + { + int cert_size = SSLUtil.MaxCerts(); + int ca_cert_size = SSLUtil.MaxCACerts(); + + Console.WriteLine("unknown option " + option); + + if (build_mode >= axtls.SSL_BUILD_ENABLE_CLIENT) + { + Console.WriteLine("usage: s_client [args ...]"); + Console.WriteLine(" -connect host:port - who to connect to " + + "(default is localhost:4433)"); + Console.WriteLine(" -verify\t- turn on peer certificate " + + "verification"); + Console.WriteLine(" -cert arg\t- certificate file to use"); + Console.WriteLine("\t\t Can repeat up to %d times", cert_size); + Console.WriteLine(" -key arg\t- Private key file to use"); + Console.WriteLine(" -CAfile arg\t- Certificate authority."); + Console.WriteLine("\t\t Can repeat up to " + ca_cert_size + + " times"); + Console.WriteLine(" -quiet\t\t- No client output"); + Console.WriteLine(" -pass\t\t- private key file pass " + + "phrase source"); + Console.WriteLine(" -reconnect\t- Drop and re-make the " + + "connection with the same Session-ID"); + + if (build_mode == axtls.SSL_BUILD_FULL_MODE) + { + Console.WriteLine(" -debug\t\t- Print more output"); + Console.WriteLine(" -state\t\t- Show state messages"); + Console.WriteLine(" -show-rsa\t- Show RSA state"); + } + } + else + { + Console.WriteLine("Change configuration to allow this feature"); + } + + Environment.Exit(1); + } + + /** + * Display what cipher we are using + */ + private void display_cipher(SSL ssl) + { + Console.Write("CIPHER is "); + + switch (ssl.GetCipherId()) + { + case axtls.SSL_AES128_SHA: + Console.WriteLine("AES128-SHA"); + break; + + case axtls.SSL_AES256_SHA: + Console.WriteLine("AES256-SHA"); + break; + + case axtls.SSL_RC4_128_SHA: + Console.WriteLine("RC4-SHA"); + break; + + case axtls.SSL_RC4_128_MD5: + Console.WriteLine("RC4-MD5"); + break; + + default: + Console.WriteLine("Unknown - " + ssl.GetCipherId()); + break; + } + } + + /** + * Display what session id we have. + */ + private void display_session_id(SSL ssl) + { + byte[] session_id = ssl.GetSessionId(); + + if (session_id.Length > 0) + { + Console.WriteLine("-----BEGIN SSL SESSION PARAMETERS-----"); + foreach (byte b in session_id) + { + Console.Write("{0:x02}", b); + } + + Console.WriteLine("\n-----END SSL SESSION PARAMETERS-----"); + } + } +} diff --git a/libs/nixio/axTLS/samples/java/Makefile b/libs/nixio/axTLS/samples/java/Makefile new file mode 100644 index 000000000..b10a79f37 --- /dev/null +++ b/libs/nixio/axTLS/samples/java/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf +include ../../config/makefile.java.conf + +all : sample +JAR=../../$(STAGE)/axtls.jar +CLASSES=../../bindings/java/classes +sample : $(JAR) + +$(JAR) : $(CLASSES)/axssl.class $(wildcard $(CLASSES)/axTLSj/*.class) + jar mcvf manifest.mf $@ -C $(CLASSES) axTLSj -C $(CLASSES) axssl.class + +JAVA_FILES=axssl.java +JAVA_CLASSES:=$(JAVA_FILES:%.java=$(CLASSES)/axTLSj/%.class) + +$(CLASSES)/%.class : %.java + javac -d $(CLASSES) -classpath $(CLASSES) $^ + +clean:: + -@rm -f $(TARGET) + diff --git a/libs/nixio/axTLS/samples/java/axssl.java b/libs/nixio/axTLS/samples/java/axssl.java new file mode 100644 index 000000000..2057f2966 --- /dev/null +++ b/libs/nixio/axTLS/samples/java/axssl.java @@ -0,0 +1,760 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * Demonstrate the use of the axTLS library in Java with a set of + * command-line parameters similar to openssl. In fact, openssl clients + * should be able to communicate with axTLS servers and visa-versa. * + * This code has various bits enabled depending on the configuration. To enable + * the most interesting version, compile with the 'full mode' enabled. + * + * To see what options you have, run the following: + * > java -jar axtls.jar s_server -? + * > java -jar axtls.jar s_client -? + * + * The axtls/axtlsj shared libraries must be in the same directory or be found + * by the OS. + */ + +import java.io.*; +import java.util.*; +import java.net.*; +import axTLSj.*; + +public class axssl +{ + /* + * Main() + */ + public static void main(String[] args) + { + if (args.length == 1 && args[0].equals("version")) + { + System.out.println("axtls.jar " + SSLUtil.version()); + System.exit(0); + } + + axssl runner = new axssl(); + + try + { + if (args.length < 1 || + (!args[0].equals("s_server") && + !args[0].equals("s_client"))) + { + runner.print_options(args.length > 0 ? args[0] : ""); + } + + int build_mode = SSLUtil.buildMode(); + + if (args[0].equals("s_server")) + runner.do_server(build_mode, args); + else + runner.do_client(build_mode, args); + } + catch (Exception e) + { + System.out.println(e); + } + } + + /* + * do_server() + */ + private void do_server(int build_mode, String[] args) + throws Exception + { + int i = 1; + int port = 4433; + int options = axtlsj.SSL_DISPLAY_CERTS; + boolean quiet = false; + String password = null; + String private_key_file = null; + + /* organise the cert/ca_cert lists */ + int cert_size = SSLUtil.maxCerts(); + int ca_cert_size = SSLUtil.maxCACerts(); + String[] cert = new String[cert_size]; + String[] ca_cert = new String[ca_cert_size]; + int cert_index = 0; + int ca_cert_index = 0; + + while (i < args.length) + { + if (args[i].equals("-accept")) + { + if (i >= args.length-1) + { + print_server_options(build_mode, args[i]); + } + + port = Integer.parseInt(args[++i]); + } + else if (args[i].equals("-quiet")) + { + quiet = true; + options &= ~(int)axtlsj.SSL_DISPLAY_CERTS; + } + else if (build_mode >= axtlsj.SSL_BUILD_SERVER_ONLY) + { + if (args[i].equals("-cert")) + { + if (i >= args.length-1 || cert_index >= cert_size) + { + print_server_options(build_mode, args[i]); + } + + cert[cert_index++] = args[++i]; + } + else if (args[i].equals("-key")) + { + if (i >= args.length-1) + { + print_server_options(build_mode, args[i]); + } + + private_key_file = args[++i]; + options |= axtlsj.SSL_NO_DEFAULT_KEY; + } + else if (args[i].equals("-pass")) + { + if (i >= args.length-1) + { + print_server_options(build_mode, args[i]); + } + + password = args[++i]; + } + else if (build_mode >= axtlsj.SSL_BUILD_ENABLE_VERIFICATION) + { + if (args[i].equals("-verify")) + { + options |= axtlsj.SSL_CLIENT_AUTHENTICATION; + } + else if (args[i].equals("-CAfile")) + { + if (i >= args.length-1 || ca_cert_index >= ca_cert_size) + { + print_server_options(build_mode, args[i]); + } + + ca_cert[ca_cert_index++] = args[++i]; + } + else if (build_mode == axtlsj.SSL_BUILD_FULL_MODE) + { + if (args[i].equals("-debug")) + { + options |= axtlsj.SSL_DISPLAY_BYTES; + } + else if (args[i].equals("-state")) + { + options |= axtlsj.SSL_DISPLAY_STATES; + } + else if (args[i].equals("-show-rsa")) + { + options |= axtlsj.SSL_DISPLAY_RSA; + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + } + else + print_server_options(build_mode, args[i]); + + i++; + } + + /* Create socket for incoming connections */ + ServerSocket server_sock = new ServerSocket(port); + + /********************************************************************** + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + **********************************************************************/ + SSLServer ssl_ctx = new SSLServer(options, + axtlsj.SSL_DEFAULT_SVR_SESS); + + if (ssl_ctx == null) + throw new Exception("Error: Server context is invalid"); + + if (private_key_file != null) + { + int obj_type = axtlsj.SSL_OBJ_RSA_KEY; + + if (private_key_file.endsWith(".p8")) + obj_type = axtlsj.SSL_OBJ_PKCS8; + else if (private_key_file.endsWith(".p12")) + obj_type = axtlsj.SSL_OBJ_PKCS12; + + if (ssl_ctx.objLoad(obj_type, + private_key_file, password) != axtlsj.SSL_OK) + { + throw new Exception("Error: Private key '" + private_key_file + + "' is undefined."); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CERT, + cert[i], null) != axtlsj.SSL_OK) + { + throw new Exception("Certificate '" + cert[i] + + "' is undefined."); + } + } + + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CACERT, + ca_cert[i], null) != axtlsj.SSL_OK) + { + throw new Exception("Certificate '" + ca_cert[i] + + "' is undefined."); + } + } + + int res; + SSLReadHolder rh = new SSLReadHolder(); + + for (;;) + { + if (!quiet) + { + System.out.println("ACCEPT"); + } + + Socket client_sock = server_sock.accept(); + + SSL ssl = ssl_ctx.connect(client_sock); + + while ((res = ssl_ctx.read(ssl, rh)) == axtlsj.SSL_OK) + { + /* check when the connection has been established */ + if (ssl.handshakeStatus() == axtlsj.SSL_OK) + break; + + /* could do something else here */ + } + + if (res == axtlsj.SSL_OK) /* connection established and ok */ + { + if (!quiet) + { + display_session_id(ssl); + display_cipher(ssl); + } + + /* now read (and display) whatever the client sends us */ + for (;;) + { + /* keep reading until we get something interesting */ + while ((res = ssl_ctx.read(ssl, rh)) == axtlsj.SSL_OK) + { + /* could do something else here */ + } + + if (res < axtlsj.SSL_OK) + { + if (!quiet) + { + System.out.println("CONNECTION CLOSED"); + } + + break; + } + + /* convert to String */ + byte[] buf = rh.getData(); + char[] str = new char[res]; + + for (i = 0; i < res; i++) + { + str[i] = (char)buf[i]; + } + + System.out.print(str); + } + } + else if (!quiet) + { + SSLUtil.displayError(res); + } + + /* client was disconnected or the handshake failed. */ + ssl.dispose(); + client_sock.close(); + } + + /* ssl_ctx.dispose(); */ + } + + /* + * do_client() + */ + private void do_client(int build_mode, String[] args) + throws Exception + { + if (build_mode < axtlsj.SSL_BUILD_ENABLE_CLIENT) + print_client_options(build_mode, args[1]); + + int i = 1, res; + int port = 4433; + boolean quiet = false; + String password = null; + int reconnect = 0; + String private_key_file = null; + String hostname = "127.0.0.1"; + + /* organise the cert/ca_cert lists */ + int cert_index = 0; + int ca_cert_index = 0; + int cert_size = SSLUtil.maxCerts(); + int ca_cert_size = SSLUtil.maxCACerts(); + String[] cert = new String[cert_size]; + String[] ca_cert = new String[ca_cert_size]; + + int options = axtlsj.SSL_SERVER_VERIFY_LATER|axtlsj.SSL_DISPLAY_CERTS; + byte[] session_id = null; + + while (i < args.length) + { + if (args[i].equals("-connect")) + { + String host_port; + + if (i >= args.length-1) + { + print_client_options(build_mode, args[i]); + } + + host_port = args[++i]; + int index_colon; + + if ((index_colon = host_port.indexOf(':')) < 0) + print_client_options(build_mode, args[i]); + + hostname = new String(host_port.toCharArray(), + 0, index_colon); + port = Integer.parseInt(new String(host_port.toCharArray(), + index_colon+1, host_port.length()-index_colon-1)); + } + else if (args[i].equals("-cert")) + { + if (i >= args.length-1 || cert_index >= cert_size) + { + print_client_options(build_mode, args[i]); + } + + cert[cert_index++] = args[++i]; + } + else if (args[i].equals("-CAfile")) + { + if (i >= args.length-1 || ca_cert_index >= ca_cert_size) + { + print_client_options(build_mode, args[i]); + } + + ca_cert[ca_cert_index++] = args[++i]; + } + else if (args[i].equals("-key")) + { + if (i >= args.length-1) + { + print_client_options(build_mode, args[i]); + } + + private_key_file = args[++i]; + options |= axtlsj.SSL_NO_DEFAULT_KEY; + } + else if (args[i].equals("-verify")) + { + options &= ~(int)axtlsj.SSL_SERVER_VERIFY_LATER; + } + else if (args[i].equals("-reconnect")) + { + reconnect = 4; + } + else if (args[i].equals("-quiet")) + { + quiet = true; + options &= ~(int)axtlsj.SSL_DISPLAY_CERTS; + } + else if (args[i].equals("-pass")) + { + if (i >= args.length-1) + { + print_server_options(build_mode, args[i]); + } + + password = args[++i]; + } + else if (build_mode == axtlsj.SSL_BUILD_FULL_MODE) + { + if (args[i].equals("-debug")) + { + options |= axtlsj.SSL_DISPLAY_BYTES; + } + else if (args[i].equals("-state")) + { + options |= axtlsj.SSL_DISPLAY_STATES; + } + else if (args[i].equals("-show-rsa")) + { + options |= axtlsj.SSL_DISPLAY_RSA; + } + else + print_client_options(build_mode, args[i]); + } + else /* don't know what this is */ + print_client_options(build_mode, args[i]); + + i++; + } + + Socket client_sock = new Socket(hostname, port); + + if (!client_sock.isConnected()) + { + System.out.println("could not connect"); + throw new Exception(); + } + + if (!quiet) + { + System.out.println("CONNECTED"); + } + + /********************************************************************** + * This is where the interesting stuff happens. Up until now we've + * just been setting up sockets etc. Now we do the SSL handshake. + **********************************************************************/ + SSLClient ssl_ctx = new SSLClient(options, + axtlsj.SSL_DEFAULT_CLNT_SESS); + + if (ssl_ctx == null) + { + throw new Exception("Error: Client context is invalid"); + } + + if (private_key_file != null) + { + int obj_type = axtlsj.SSL_OBJ_RSA_KEY; + + if (private_key_file.endsWith(".p8")) + obj_type = axtlsj.SSL_OBJ_PKCS8; + else if (private_key_file.endsWith(".p12")) + obj_type = axtlsj.SSL_OBJ_PKCS12; + + if (ssl_ctx.objLoad(obj_type, + private_key_file, password) != axtlsj.SSL_OK) + { + throw new Exception("Error: Private key '" + private_key_file + + "' is undefined."); + } + } + + for (i = 0; i < cert_index; i++) + { + if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CERT, + cert[i], null) != axtlsj.SSL_OK) + { + throw new Exception("Certificate '" + cert[i] + + "' is undefined."); + } + } + + for (i = 0; i < ca_cert_index; i++) + { + if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CACERT, + ca_cert[i], null) != axtlsj.SSL_OK) + { + throw new Exception("Certificate '" + ca_cert[i] + + "' is undefined."); + } + } + + SSL ssl = null; + + /* Try session resumption? */ + if (reconnect > 0) + { + while (reconnect-- > 0) + { + ssl = ssl_ctx.connect(client_sock, session_id); + + if ((res = ssl.handshakeStatus()) != axtlsj.SSL_OK) + { + if (!quiet) + { + SSLUtil.displayError(res); + } + + ssl.dispose(); + throw new Exception(); + } + + display_session_id(ssl); + session_id = ssl.getSessionId(); + + if (reconnect > 0) + { + ssl.dispose(); + client_sock.close(); + + /* and reconnect */ + client_sock = new Socket(hostname, port); + } + } + } + else + { + ssl = ssl_ctx.connect(client_sock, null); + } + + /* check the return status */ + if ((res = ssl.handshakeStatus()) != axtlsj.SSL_OK) + { + if (!quiet) + { + SSLUtil.displayError(res); + } + + throw new Exception(); + } + + if (!quiet) + { + String common_name = + ssl.getCertificateDN(axtlsj.SSL_X509_CERT_COMMON_NAME); + + if (common_name != null) + { + System.out.println("Common Name:\t\t\t" + common_name); + } + + display_session_id(ssl); + display_cipher(ssl); + } + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + + for (;;) + { + String user_input = in.readLine(); + + if (user_input == null) + break; + + byte[] buf = new byte[user_input.length()+2]; + buf[buf.length-2] = (byte)'\n'; /* add the carriage return */ + buf[buf.length-1] = 0; /* null terminate */ + + for (i = 0; i < buf.length-2; i++) + { + buf[i] = (byte)user_input.charAt(i); + } + + if ((res = ssl_ctx.write(ssl, buf)) < axtlsj.SSL_OK) + { + if (!quiet) + { + SSLUtil.displayError(res); + } + + break; + } + } + + ssl_ctx.dispose(); + } + + /** + * We've had some sort of command-line error. Print out the basic options. + */ + private void print_options(String option) + { + System.out.println("axssl: Error: '" + option + + "' is an invalid command."); + System.out.println("usage: axtlsj.jar [s_server|s_client|version] " + + "[args ...]"); + System.exit(1); + } + + /** + * We've had some sort of command-line error. Print out the server options. + */ + private void print_server_options(int build_mode, String option) + { + int cert_size = SSLUtil.maxCerts(); + int ca_cert_size = SSLUtil.maxCACerts(); + + System.out.println("unknown option " + option); + System.out.println("usage: s_server [args ...]"); + System.out.println(" -accept arg\t- port to accept on (default " + + "is 4433)"); + System.out.println(" -quiet\t\t- No server output"); + + if (build_mode >= axtlsj.SSL_BUILD_SERVER_ONLY) + { + System.out.println(" -cert arg\t- certificate file to add (in " + + "addition to default) to chain -"); + System.out.println("\t\t Can repeat up to " + cert_size + " times"); + System.out.println(" -key arg\t- Private key file to use"); + System.out.println(" -pass\t\t- private key file pass phrase source"); + } + + if (build_mode >= axtlsj.SSL_BUILD_ENABLE_VERIFICATION) + { + System.out.println(" -verify\t- turn on peer certificate " + + "verification"); + System.out.println(" -CAfile arg\t- Certificate authority. "); + System.out.println("\t\t Can repeat up to " + + ca_cert_size + " times"); + } + + if (build_mode == axtlsj.SSL_BUILD_FULL_MODE) + { + System.out.println(" -debug\t\t- Print more output"); + System.out.println(" -state\t\t- Show state messages"); + System.out.println(" -show-rsa\t- Show RSA state"); + } + + System.exit(1); + } + + /** + * We've had some sort of command-line error. Print out the client options. + */ + private void print_client_options(int build_mode, String option) + { + int cert_size = SSLUtil.maxCerts(); + int ca_cert_size = SSLUtil.maxCACerts(); + + System.out.println("unknown option " + option); + + if (build_mode >= axtlsj.SSL_BUILD_ENABLE_CLIENT) + { + System.out.println("usage: s_client [args ...]"); + System.out.println(" -connect host:port - who to connect to " + + "(default is localhost:4433)"); + System.out.println(" -verify\t- turn on peer certificate " + + "verification"); + System.out.println(" -cert arg\t- certificate file to use"); + System.out.println(" -key arg\t- Private key file to use"); + System.out.println("\t\t Can repeat up to " + cert_size + + " times"); + System.out.println(" -CAfile arg\t- Certificate authority."); + System.out.println("\t\t Can repeat up to " + ca_cert_size + + " times"); + System.out.println(" -quiet\t\t- No client output"); + System.out.println(" -pass\t\t- private key file pass " + + "phrase source"); + System.out.println(" -reconnect\t- Drop and re-make the " + + "connection with the same Session-ID"); + + if (build_mode == axtlsj.SSL_BUILD_FULL_MODE) + { + System.out.println(" -debug\t\t- Print more output"); + System.out.println(" -state\t\t- Show state messages"); + System.out.println(" -show-rsa\t- Show RSA state"); + } + } + else + { + System.out.println("Change configuration to allow this feature"); + } + + System.exit(1); + } + + /** + * Display what cipher we are using + */ + private void display_cipher(SSL ssl) + { + System.out.print("CIPHER is "); + + byte ciph_id = ssl.getCipherId(); + + if (ciph_id == axtlsj.SSL_AES128_SHA) + System.out.println("AES128-SHA"); + else if (ciph_id == axtlsj.SSL_AES256_SHA) + System.out.println("AES256-SHA"); + else if (ciph_id == axtlsj.SSL_RC4_128_SHA) + System.out.println("RC4-SHA"); + else if (ciph_id == axtlsj.SSL_RC4_128_MD5) + System.out.println("RC4-MD5"); + else + System.out.println("Unknown - " + ssl.getCipherId()); + } + + public char toHexChar(int i) + { + if ((0 <= i) && (i <= 9 )) + return (char)('0' + i); + else + return (char)('a' + (i-10)); + } + + public void bytesToHex(byte[] data) + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < data.length; i++ ) + { + buf.append(toHexChar((data[i]>>>4)&0x0F)); + buf.append(toHexChar(data[i]&0x0F)); + } + + System.out.println(buf); + } + + + /** + * Display what session id we have. + */ + private void display_session_id(SSL ssl) + { + byte[] session_id = ssl.getSessionId(); + + if (session_id.length > 0) + { + System.out.println("-----BEGIN SSL SESSION PARAMETERS-----"); + bytesToHex(session_id); + System.out.println("-----END SSL SESSION PARAMETERS-----"); + } + } +} diff --git a/libs/nixio/axTLS/samples/java/manifest.mf b/libs/nixio/axTLS/samples/java/manifest.mf new file mode 100644 index 000000000..b906ed29e --- /dev/null +++ b/libs/nixio/axTLS/samples/java/manifest.mf @@ -0,0 +1 @@ +Main-Class: axssl diff --git a/libs/nixio/axTLS/samples/lua/Makefile b/libs/nixio/axTLS/samples/lua/Makefile new file mode 100644 index 000000000..a460da3c5 --- /dev/null +++ b/libs/nixio/axTLS/samples/lua/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf + +all: samples +TARGET=../../$(STAGE)/axssl.lua +samples: $(TARGET) + +$(TARGET): axssl.lua + install $< $@ + +clean:: + -@rm -f $(TARGET) + diff --git a/libs/nixio/axTLS/samples/lua/axssl.lua b/libs/nixio/axTLS/samples/lua/axssl.lua new file mode 100755 index 000000000..6ea26b69d --- /dev/null +++ b/libs/nixio/axTLS/samples/lua/axssl.lua @@ -0,0 +1,562 @@ +#!/usr/local/bin/lua + +-- +-- Copyright (c) 2007, Cameron Rich +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * 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. +-- * Neither the name of the axTLS project nor the names of its +-- contributors may be used to endorse or promote products derived +-- from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "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 THE COPYRIGHT OWNER 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. +-- + +-- +-- Demonstrate the use of the axTLS library in Lua with a set of +-- command-line parameters similar to openssl. In fact, openssl clients +-- should be able to communicate with axTLS servers and visa-versa. +-- +-- This code has various bits enabled depending on the configuration. To enable +-- the most interesting version, compile with the 'full mode' enabled. +-- +-- To see what options you have, run the following: +-- > [lua] axssl s_server -? +-- > [lua] axssl s_client -? +-- +-- The axtls/axtlsl shared libraries must be in the same directory or be found +-- by the OS. +-- +-- +require "bit" +require("axtlsl") +local socket = require("socket") + +-- print version? +if #arg == 1 and arg[1] == "version" then + print("axssl.lua "..axtlsl.ssl_version()) + os.exit(1) +end + +-- +-- We've had some sort of command-line error. Print out the basic options. +-- +function print_options(option) + print("axssl: Error: '"..option.."' is an invalid command.") + print("usage: axssl [s_server|s_client|version] [args ...]") + os.exit(1) +end + +-- +-- We've had some sort of command-line error. Print out the server options. +-- +function print_server_options(build_mode, option) + local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET) + local ca_cert_size = axtlsl.ssl_get_config( + axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET) + + print("unknown option "..option) + print("usage: s_server [args ...]") + print(" -accept\t- port to accept on (default is 4433)") + print(" -quiet\t\t- No server output") + + if build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then + print(" -cert arg\t- certificate file to add (in addition to ".. + "default) to chain -") + print("\t\t Can repeat up to "..cert_size.." times") + print(" -key arg\t- Private key file to use - default DER format") + print(" -pass\t\t- private key file pass phrase source") + end + + if build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then + print(" -verify\t- turn on peer certificate verification") + print(" -CAfile arg\t- Certificate authority - default DER format") + print("\t\t Can repeat up to "..ca_cert_size.." times") + end + + if build_mode == axtlsl.SSL_BUILD_FULL_MODE then + print(" -debug\t\t- Print more output") + print(" -state\t\t- Show state messages") + print(" -show-rsa\t- Show RSA state") + end + + os.exit(1) +end + +-- +-- We've had some sort of command-line error. Print out the client options. +-- +function print_client_options(build_mode, option) + local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET) + local ca_cert_size = axtlsl.ssl_get_config( + axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET) + + print("unknown option "..option) + + if build_mode >= axtlsl.SSL_BUILD_ENABLE_CLIENT then + print("usage: s_client [args ...]") + print(" -connect host:port - who to connect to (default ".. + "is localhost:4433)") + print(" -verify\t- turn on peer certificate verification") + print(" -cert arg\t- certificate file to use - default DER format") + print(" -key arg\t- Private key file to use - default DER format") + print("\t\t Can repeat up to "..cert_size.." times") + print(" -CAfile arg\t- Certificate authority - default DER format") + print("\t\t Can repeat up to "..ca_cert_size.."times") + print(" -quiet\t\t- No client output") + print(" -pass\t\t- private key file pass phrase source") + print(" -reconnect\t- Drop and re-make the connection ".. + "with the same Session-ID") + + if build_mode == axtlsl.SSL_BUILD_FULL_MODE then + print(" -debug\t\t- Print more output") + print(" -state\t\t- Show state messages") + print(" -show-rsa\t- Show RSA state") + end + else + print("Change configuration to allow this feature") + end + + os.exit(1) +end + +-- Implement the SSL server logic. +function do_server(build_mode) + local i = 2 + local v + local port = 4433 + local options = axtlsl.SSL_DISPLAY_CERTS + local quiet = false + local password = "" + local private_key_file = nil + local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET) + local ca_cert_size = axtlsl. + ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET) + local cert = {} + local ca_cert = {} + + while i <= #arg do + if arg[i] == "-accept" then + if i >= #arg then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + port = arg[i] + elseif arg[i] == "-quiet" then + quiet = true + options = bit.band(options, bit.bnot(axtlsl.SSL_DISPLAY_CERTS)) + elseif build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then + if arg[i] == "-cert" then + if i >= #arg or #cert >= cert_size then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + table.insert(cert, arg[i]) + elseif arg[i] == "-key" then + if i >= #arg then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + private_key_file = arg[i] + options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY) + elseif arg[i] == "-pass" then + if i >= #arg then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + password = arg[i] + elseif build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then + if arg[i] == "-verify" then + options = bit.bor(options, axtlsl.SSL_CLIENT_AUTHENTICATION) + elseif arg[i] == "-CAfile" then + if i >= #arg or #ca_cert >= ca_cert_size then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + table.insert(ca_cert, arg[i]) + elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then + if arg[i] == "-debug" then + options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES) + elseif arg[i] == "-state" then + options = bit.bor(options, axtlsl.SSL_DISPLAY_STATES) + elseif arg[i] == "-show-rsa" then + options = bit.bor(options, axtlsl.SSL_DISPLAY_RSA) + else + print_server_options(build_mode, arg[i]) + end + else + print_server_options(build_mode, arg[i]) + end + else + print_server_options(build_mode, arg[i]) + end + else + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + end + + -- Create socket for incoming connections + local server_sock = socket.try(socket.bind("*", port)) + + --------------------------------------------------------------------------- + -- This is where the interesting stuff happens. Up until now we've + -- just been setting up sockets etc. Now we do the SSL handshake. + --------------------------------------------------------------------------- + local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_SVR_SESS) + if ssl_ctx == nil then error("Error: Server context is invalid") end + + if private_key_file ~= nil then + local obj_type = axtlsl.SSL_OBJ_RSA_KEY + + if string.find(private_key_file, ".p8") then + obj_type = axtlsl.SSL_OBJ_PKCS8 + end + + if string.find(private_key_file, ".p12") then + obj_type = axtlsl.SSL_OBJ_PKCS12 + end + + if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file, + password) ~= axtlsl.SSL_OK then + error("Private key '" .. private_key_file .. "' is undefined.") + end + end + + for _, v in ipairs(cert) do + if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~= + axtlsl.SSL_OK then + error("Certificate '"..v .. "' is undefined.") + end + end + + for _, v in ipairs(ca_cert) do + if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~= + axtlsl.SSL_OK then + error("Certificate '"..v .."' is undefined.") + end + end + + while true do + if not quiet then print("ACCEPT") end + local client_sock = server_sock:accept(); + local ssl = axtlsl.ssl_server_new(ssl_ctx, client_sock:getfd()) + + -- do the actual SSL handshake + local connected = false + local res + local buf + + while true do + socket.select({client_sock}, nil) + res, buf = axtlsl.ssl_read(ssl) + + if res == axtlsl.SSL_OK then -- connection established and ok + if axtlsl.ssl_handshake_status(ssl) == axtlsl.SSL_OK then + if not quiet and not connected then + display_session_id(ssl) + display_cipher(ssl) + end + connected = true + end + end + + if res > axtlsl.SSL_OK then + for _, v in ipairs(buf) do + io.write(string.format("%c", v)) + end + elseif res < axtlsl.SSL_OK then + if not quiet then + axtlsl.ssl_display_error(res) + end + break + end + end + + -- client was disconnected or the handshake failed. + print("CONNECTION CLOSED") + axtlsl.ssl_free(ssl) + client_sock:close() + end + + axtlsl.ssl_ctx_free(ssl_ctx) +end + +-- +-- Implement the SSL client logic. +-- +function do_client(build_mode) + local i = 2 + local v + local port = 4433 + local options = + bit.bor(axtlsl.SSL_SERVER_VERIFY_LATER, axtlsl.SSL_DISPLAY_CERTS) + local private_key_file = nil + local reconnect = 0 + local quiet = false + local password = "" + local session_id = {} + local host = "127.0.0.1" + local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET) + local ca_cert_size = axtlsl. + ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET) + local cert = {} + local ca_cert = {} + + while i <= #arg do + if arg[i] == "-connect" then + if i >= #arg then + print_client_options(build_mode, arg[i]) + end + + i = i + 1 + local t = string.find(arg[i], ":") + host = string.sub(arg[i], 1, t-1) + port = string.sub(arg[i], t+1) + elseif arg[i] == "-cert" then + if i >= #arg or #cert >= cert_size then + print_client_options(build_mode, arg[i]) + end + + i = i + 1 + table.insert(cert, arg[i]) + elseif arg[i] == "-key" then + if i >= #arg then + print_client_options(build_mode, arg[i]) + end + + i = i + 1 + private_key_file = arg[i] + options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY) + elseif arg[i] == "-CAfile" then + if i >= #arg or #ca_cert >= ca_cert_size then + print_client_options(build_mode, arg[i]) + end + + i = i + 1 + table.insert(ca_cert, arg[i]) + elseif arg[i] == "-verify" then + options = bit.band(options, + bit.bnot(axtlsl.SSL_SERVER_VERIFY_LATER)) + elseif arg[i] == "-reconnect" then + reconnect = 4 + elseif arg[i] == "-quiet" then + quiet = true + options = bit.band(options, bnot(axtlsl.SSL_DISPLAY_CERTS)) + elseif arg[i] == "-pass" then + if i >= #arg then + print_server_options(build_mode, arg[i]) + end + + i = i + 1 + password = arg[i] + elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then + if arg[i] == "-debug" then + options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES) + elseif arg[i] == "-state" then + options = bit.bor(axtlsl.SSL_DISPLAY_STATES) + elseif arg[i] == "-show-rsa" then + options = bit.bor(axtlsl.SSL_DISPLAY_RSA) + else -- don't know what this is + print_client_options(build_mode, arg[i]) + end + else -- don't know what this is + print_client_options(build_mode, arg[i]) + end + + i = i + 1 + end + + local client_sock = socket.try(socket.connect(host, port)) + local ssl + local res + + if not quiet then print("CONNECTED") end + + --------------------------------------------------------------------------- + -- This is where the interesting stuff happens. Up until now we've + -- just been setting up sockets etc. Now we do the SSL handshake. + --------------------------------------------------------------------------- + local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_CLNT_SESS) + + if ssl_ctx == nil then + error("Error: Client context is invalid") + end + + if private_key_file ~= nil then + local obj_type = axtlsl.SSL_OBJ_RSA_KEY + + if string.find(private_key_file, ".p8") then + obj_type = axtlsl.SSL_OBJ_PKCS8 + end + + if string.find(private_key_file, ".p12") then + obj_type = axtlsl.SSL_OBJ_PKCS12 + end + + if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file, + password) ~= axtlsl.SSL_OK then + error("Private key '"..private_key_file.."' is undefined.") + end + end + + for _, v in ipairs(cert) do + if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~= + axtlsl.SSL_OK then + error("Certificate '"..v .. "' is undefined.") + end + end + + for _, v in ipairs(ca_cert) do + if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~= + axtlsl.SSL_OK then + error("Certificate '"..v .."' is undefined.") + end + end + + -- Try session resumption? + if reconnect ~= 0 then + local session_id = nil + local sess_id_size = 0 + + while reconnect > 0 do + reconnect = reconnect - 1 + ssl = axtlsl.ssl_client_new(ssl_ctx, + client_sock:getfd(), session_id, sess_id_size) + + res = axtlsl.ssl_handshake_status(ssl) + if res ~= axtlsl.SSL_OK then + if not quiet then axtlsl.ssl_display_error(res) end + axtlsl.ssl_free(ssl) + os.exit(1) + end + + display_session_id(ssl) + session_id = axtlsl.ssl_get_session_id(ssl) + sess_id_size = axtlsl.ssl_get_session_id_size(ssl) + + if reconnect > 0 then + axtlsl.ssl_free(ssl) + client_sock:close() + client_sock = socket.try(socket.connect(host, port)) + end + + end + else + ssl = axtlsl.ssl_client_new(ssl_ctx, client_sock:getfd(), nil, 0) + end + + -- check the return status + res = axtlsl.ssl_handshake_status(ssl) + if res ~= axtlsl.SSL_OK then + if not quiet then axtlsl.ssl_display_error(res) end + os.exit(1) + end + + if not quiet then + local common_name = axtlsl.ssl_get_cert_dn(ssl, + axtlsl.SSL_X509_CERT_COMMON_NAME) + + if common_name ~= nil then + print("Common Name:\t\t\t"..common_name) + end + + display_session_id(ssl) + display_cipher(ssl) + end + + while true do + local line = io.read() + if line == nil then break end + local bytes = {} + + for i = 1, #line do + bytes[i] = line.byte(line, i) + end + + bytes[#line+1] = 10 -- add carriage return, null + bytes[#line+2] = 0 + + res = axtlsl.ssl_write(ssl, bytes, #bytes) + if res < axtlsl.SSL_OK then + if not quiet then axtlsl.ssl_display_error(res) end + break + end + end + + axtlsl.ssl_ctx_free(ssl_ctx) + client_sock:close() +end + +-- +-- Display what cipher we are using +-- +function display_cipher(ssl) + io.write("CIPHER is ") + local cipher_id = axtlsl.ssl_get_cipher_id(ssl) + + if cipher_id == axtlsl.SSL_AES128_SHA then + print("AES128-SHA") + elseif cipher_id == axtlsl.SSL_AES256_SHA then + print("AES256-SHA") + elseif axtlsl.SSL_RC4_128_SHA then + print("RC4-SHA") + elseif axtlsl.SSL_RC4_128_MD5 then + print("RC4-MD5") + else + print("Unknown - "..cipher_id) + end +end + +-- +-- Display what session id we have. +-- +function display_session_id(ssl) + local session_id = axtlsl.ssl_get_session_id(ssl) + local v + + if #session_id > 0 then + print("-----BEGIN SSL SESSION PARAMETERS-----") + for _, v in ipairs(session_id) do + io.write(string.format("%02x", v)) + end + print("\n-----END SSL SESSION PARAMETERS-----") + end +end + +-- +-- Main entry point. Doesn't do much except works out whether we are a client +-- or a server. +-- +if #arg == 0 or (arg[1] ~= "s_server" and arg[1] ~= "s_client") then + print_options(#arg > 0 and arg[1] or "") +end + +local build_mode = axtlsl.ssl_get_config(axtlsl.SSL_BUILD_MODE) +_ = arg[1] == "s_server" and do_server(build_mode) or do_client(build_mode) +os.exit(0) + diff --git a/libs/nixio/axTLS/samples/perl/Makefile b/libs/nixio/axTLS/samples/perl/Makefile new file mode 100644 index 000000000..5200c4302 --- /dev/null +++ b/libs/nixio/axTLS/samples/perl/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf + +all: samples +TARGET=../../$(STAGE)/axssl.pl +samples: $(TARGET) + +$(TARGET): axssl.pl + install $< $@ + +clean:: + -@rm -f $(TARGET) + diff --git a/libs/nixio/axTLS/samples/perl/axssl.pl b/libs/nixio/axTLS/samples/perl/axssl.pl new file mode 100755 index 000000000..e49d52270 --- /dev/null +++ b/libs/nixio/axTLS/samples/perl/axssl.pl @@ -0,0 +1,634 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +# +# Demonstrate the use of the axTLS library in Perl with a set of +# command-line parameters similar to openssl. In fact, openssl clients +# should be able to communicate with axTLS servers and visa-versa. +# +# This code has various bits enabled depending on the configuration. To enable +# the most interesting version, compile with the 'full mode' enabled. +# +# To see what options you have, run the following: +# > [perl] axssl s_server -? +# > [perl] axssl s_client -? +# +# The axtls/axtlsp shared libraries must be in the same directory or be found +# by the OS. axtlsp.pm must be in this directory or be in @INC. +# +# Under Win32, ActivePerl was used (see +# http://www.activestate.com/Products/ActivePerl/?mp=1) +# +use axtlsp; +use IO::Socket; + +# To get access to Win32 file descriptor stuff +my $is_win32 = 0; + +if ($^O eq "MSWin32") +{ + eval("use Win32API::File 0.08 qw( :ALL )"); + $is_win32 = 1; +} + +use strict; + +# +# Win32 has some problems with socket handles +# +sub get_native_sock +{ + my ($sock) = @_; + return $is_win32 ? FdGetOsFHandle($sock) : $sock; +} + +# print version? +if ($#ARGV == 0 && $ARGV[0] eq "version") +{ + printf("axssl.pl ".axtlsp::ssl_version()."\n"); + exit 0; +} + +# +# Main entry point. Doesn't do much except works out whether we are a client +# or a server. +# +print_options($#ARGV > -1 ? $ARGV[0] : "") + if ($#ARGV < 0 || ($ARGV[0] ne "s_server" && $ARGV[0] ne "s_client")); + + +# Cygwin/Win32 issue - flush our output continuously +select STDOUT; +local $|=1; + +my $build_mode = axtlsp::ssl_get_config($axtlsp::SSL_BUILD_MODE); +$ARGV[0] eq "s_server" ? do_server($build_mode) : do_client($build_mode); + +# +# Implement the SSL server logic. +# +sub do_server +{ + my ($build_mode) = @_; + my $i = 1; + my $port = 4433; + my $options = $axtlsp::SSL_DISPLAY_CERTS; + my $quiet = 0; + my $password = undef; + my $private_key_file = undef; + my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET); + my $ca_cert_size = axtlsp::ssl_get_config( + $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET); + my @cert; + my @ca_cert; + + while ($i <= $#ARGV) + { + if ($ARGV[$i] eq "-accept") + { + print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + $port = $ARGV[++$i]; + } + elsif ($ARGV[$i] eq "-quiet") + { + $quiet = 1; + $options &= ~$axtlsp::SSL_DISPLAY_CERTS; + } + elsif ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY) + { + if ($ARGV[$i] eq "-cert") + { + print_server_options($build_mode, $ARGV[$i]) + if $i >= $#ARGV || $#cert >= $cert_size-1; + + push @cert, $ARGV[++$i]; + } + elsif ($ARGV[$i] eq "-key") + { + print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + $private_key_file = $ARGV[++$i]; + $options |= $axtlsp::SSL_NO_DEFAULT_KEY; + } + elsif ($ARGV[$i] eq "-pass") + { + print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + $password = $ARGV[++$i]; + } + elsif ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION) + { + if ($ARGV[$i] eq "-verify") + { + $options |= $axtlsp::SSL_CLIENT_AUTHENTICATION; + } + elsif ($ARGV[$i] eq "-CAfile") + { + print_server_options($build_mode, $ARGV[$i]) + if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1; + push @ca_cert, $ARGV[++$i]; + } + elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE) + { + if ($ARGV[$i] eq "-debug") + { + $options |= $axtlsp::SSL_DISPLAY_BYTES; + } + elsif ($ARGV[$i] eq "-state") + { + $options |= $axtlsp::SSL_DISPLAY_STATES; + } + elsif ($ARGV[$i] eq "-show-rsa") + { + $options |= $axtlsp::SSL_DISPLAY_RSA; + } + else + { + print_server_options($build_mode, $ARGV[$i]); + } + } + else + { + print_server_options($build_mode, $ARGV[$i]); + } + } + else + { + print_server_options($build_mode, $ARGV[$i]); + } + } + else + { + print_server_options($build_mode, $ARGV[$i]); + } + + $i++; + } + + # Create socket for incoming connections + my $server_sock = IO::Socket::INET->new(Proto => 'tcp', + LocalPort => $port, + Listen => 1, + Reuse => 1) or die $!; + + ########################################################################### + # This is where the interesting stuff happens. Up until now we've + # just been setting up sockets etc. Now we do the SSL handshake. + ########################################################################### + my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_SVR_SESS); + die "Error: Server context is invalid" if not defined $ssl_ctx; + + if (defined $private_key_file) + { + my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY; + + $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/; + $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/; + + die "Private key '$private_key_file' is undefined." if + axtlsp::ssl_obj_load($ssl_ctx, $obj_type, + $private_key_file, $password); + } + + foreach (@cert) + { + die "Certificate '$_' is undefined." + if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT, + $_, undef) != $axtlsp::SSL_OK; + } + + foreach (@ca_cert) + { + die "Certificate '$_' is undefined." + if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT, + $_, undef) != $axtlsp::SSL_OK; + } + + for (;;) + { + printf("ACCEPT\n") if not $quiet; + my $client_sock = $server_sock->accept; + my $native_sock = get_native_sock($client_sock->fileno); + + # This doesn't work in Win32 - need to get file descriptor from socket. + my $ssl = axtlsp::ssl_server_new($ssl_ctx, $native_sock); + + # do the actual SSL handshake + my $res; + my $buf; + my $connected = 0; + + while (1) + { + ($res, $buf) = axtlsp::ssl_read($ssl, undef); + last if $res < $axtlsp::SSL_OK; + + if ($res == $axtlsp::SSL_OK) # connection established and ok + { + if (axtlsp::ssl_handshake_status($ssl) == $axtlsp::SSL_OK) + { + if (!$quiet && !$connected) + { + display_session_id($ssl); + display_cipher($ssl); + } + + $connected = 1; + } + } + + if ($res > $axtlsp::SSL_OK) + { + printf($$buf); + } + elsif ($res < $axtlsp::SSL_OK) + { + axtlsp::ssl_display_error($res) if not $quiet; + last; + } + } + + # client was disconnected or the handshake failed. + printf("CONNECTION CLOSED\n") if not $quiet; + axtlsp::ssl_free($ssl); + $client_sock->close; + } + + axtlsp::ssl_ctx_free($ssl_ctx); +} + +# +# Implement the SSL client logic. +# +sub do_client +{ + my ($build_mode) = @_; + my $i = 1; + my $port = 4433; + my $options = $axtlsp::SSL_SERVER_VERIFY_LATER|$axtlsp::SSL_DISPLAY_CERTS; + my $private_key_file = undef; + my $reconnect = 0; + my $quiet = 0; + my $password = undef; + my @session_id; + my $host = "127.0.0.1"; + my @cert; + my @ca_cert; + my $cert_size = axtlsp::ssl_get_config( + $axtlsp::SSL_MAX_CERT_CFG_OFFSET); + my $ca_cert_size = axtlsp::ssl_get_config( + $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET); + + while ($i <= $#ARGV) + { + if ($ARGV[$i] eq "-connect") + { + print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + ($host, $port) = split(':', $ARGV[++$i]); + } + elsif ($ARGV[$i] eq "-cert") + { + print_client_options($build_mode, $ARGV[$i]) + if $i >= $#ARGV || $#cert >= $cert_size-1; + + push @cert, $ARGV[++$i]; + } + elsif ($ARGV[$i] eq "-key") + { + print_client_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + $private_key_file = $ARGV[++$i]; + $options |= $axtlsp::SSL_NO_DEFAULT_KEY; + } + elsif ($ARGV[$i] eq "-CAfile") + { + print_client_options($build_mode, $ARGV[$i]) + if $i >= $#ARGV || $#ca_cert >= $ca_cert_size-1; + + push @ca_cert, $ARGV[++$i]; + } + elsif ($ARGV[$i] eq "-verify") + { + $options &= ~$axtlsp::SSL_SERVER_VERIFY_LATER; + } + elsif ($ARGV[$i] eq "-reconnect") + { + $reconnect = 4; + } + elsif ($ARGV[$i] eq "-quiet") + { + $quiet = 1; + $options &= ~$axtlsp::SSL_DISPLAY_CERTS; + } + elsif ($ARGV[$i] eq "-pass") + { + print_server_options($build_mode, $ARGV[$i]) if $i >= $#ARGV; + $password = $ARGV[++$i]; + } + elsif ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE) + { + if ($ARGV[$i] eq "-debug") + { + $options |= $axtlsp::SSL_DISPLAY_BYTES; + } + elsif ($ARGV[$i] eq "-state") + { + $options |= $axtlsp::SSL_DISPLAY_STATES; + } + elsif ($ARGV[$i] eq "-show-rsa") + { + $options |= $axtlsp::SSL_DISPLAY_RSA; + } + else # don't know what this is + { + print_client_options($build_mode, $ARGV[$i]); + } + } + else # don't know what this is + { + print_client_options($build_mode, $ARGV[$i]); + } + + $i++; + } + + my $client_sock = new IO::Socket::INET ( + PeerAddr => $host, PeerPort => $port, Proto => 'tcp') + || die ("no socket: $!"); + my $ssl; + my $res; + my $native_sock = get_native_sock($client_sock->fileno); + + printf("CONNECTED\n") if not $quiet; + + ########################################################################### + # This is where the interesting stuff happens. Up until now we've + # just been setting up sockets etc. Now we do the SSL handshake. + ########################################################################### + my $ssl_ctx = axtlsp::ssl_ctx_new($options, $axtlsp::SSL_DEFAULT_CLNT_SESS); + die "Error: Client context is invalid" if not defined $ssl_ctx; + + if (defined $private_key_file) + { + my $obj_type = $axtlsp::SSL_OBJ_RSA_KEY; + + $obj_type = $axtlsp::SSL_OBJ_PKCS8 if $private_key_file =~ /.p8$/; + $obj_type = $axtlsp::SSL_OBJ_PKCS12 if $private_key_file =~ /.p12$/; + + die "Private key '$private_key_file' is undefined." if + axtlsp::ssl_obj_load($ssl_ctx, $obj_type, + $private_key_file, $password); + } + + foreach (@cert) + { + die "Certificate '$_' is undefined." + if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CERT, + $_, undef) != $axtlsp::SSL_OK; + } + + foreach (@ca_cert) + { + die "Certificate '$_' is undefined." + if axtlsp::ssl_obj_load($ssl_ctx, $axtlsp::SSL_OBJ_X509_CACERT, + $_, undef) != $axtlsp::SSL_OK; + } + + # Try session resumption? + if ($reconnect) + { + my $session_id = undef; + my $sess_id_size = 0; + + while ($reconnect--) + { + $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock, + $session_id, $sess_id_size); + + $res = axtlsp::ssl_handshake_status($ssl); + if ($res != $axtlsp::SSL_OK) + { + axtlsp::ssl_display_error($res) if !$quiet; + axtlsp::ssl_free($ssl); + exit 1; + } + + display_session_id($ssl); + $session_id = axtlsp::ssl_get_session_id($ssl); + + if ($reconnect) + { + axtlsp::ssl_free($ssl); + $client_sock->close; + $client_sock = new IO::Socket::INET ( + PeerAddr => $host, PeerPort => $port, Proto => 'tcp') + || die ("no socket: $!"); + + } + } + } + else + { + $ssl = axtlsp::ssl_client_new($ssl_ctx, $native_sock, undef, 0); + } + + # check the return status + $res = axtlsp::ssl_handshake_status($ssl); + if ($res != $axtlsp::SSL_OK) + { + axtlsp::ssl_display_error($res) if not $quiet; + exit 1; + } + + if (!$quiet) + { + my $common_name = axtlsp::ssl_get_cert_dn($ssl, + $axtlsp::SSL_X509_CERT_COMMON_NAME); + + printf("Common Name:\t\t\t%s\n", $common_name) if defined $common_name; + display_session_id($ssl); + display_cipher($ssl); + } + + while () + { + my $cstring = pack("a*x", $_); # add null terminator + $res = axtlsp::ssl_write($ssl, \$cstring, length($cstring)); + if ($res < $axtlsp::SSL_OK) + { + axtlsp::ssl_display_error($res) if not $quiet; + last; + } + } + + axtlsp::ssl_ctx_free($ssl_ctx); + $client_sock->close; +} + +# +# We've had some sort of command-line error. Print out the basic options. +# +sub print_options +{ + my ($option) = @_; + printf("axssl: Error: '%s' is an invalid command.\n", $option); + printf("usage: axssl [s_server|s_client|version] [args ...]\n"); + exit 1; +} + +# +# We've had some sort of command-line error. Print out the server options. +# +sub print_server_options +{ + my ($build_mode, $option) = @_; + my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET); + my $ca_cert_size = axtlsp::ssl_get_config( + $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET); + + printf("unknown option %s\n", $option); + printf("usage: s_server [args ...]\n"); + printf(" -accept arg\t- port to accept on (default is 4433)\n"); + printf(" -quiet\t\t- No server output\n"); + + if ($build_mode >= $axtlsp::SSL_BUILD_SERVER_ONLY) + { + printf(" -cert arg\t- certificate file to add (in addition to default)". + " to chain -\n". + "\t\t Can repeat up to %d times\n", $cert_size); + printf(" -key arg\t- Private key file to use - default DER format\n"); + printf(" -pass\t\t- private key file pass phrase source\n"); + } + + if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_VERIFICATION) + { + printf(" -verify\t- turn on peer certificate verification\n"); + printf(" -CAfile arg\t- Certificate authority - default DER format\n"); + printf("\t\t Can repeat up to %d times\n", $ca_cert_size); + } + + if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE) + { + printf(" -debug\t\t- Print more output\n"); + printf(" -state\t\t- Show state messages\n"); + printf(" -show-rsa\t- Show RSA state\n"); + } + + exit 1; +} + +# +# We've had some sort of command-line error. Print out the client options. +# +sub print_client_options +{ + my ($build_mode, $option) = @_; + my $cert_size = axtlsp::ssl_get_config($axtlsp::SSL_MAX_CERT_CFG_OFFSET); + my $ca_cert_size = axtlsp::ssl_get_config( + $axtlsp::SSL_MAX_CA_CERT_CFG_OFFSET); + + printf("unknown option %s\n", $option); + + if ($build_mode >= $axtlsp::SSL_BUILD_ENABLE_CLIENT) + { + printf("usage: s_client [args ...]\n"); + printf(" -connect host:port - who to connect to (default ". + "is localhost:4433)\n"); + printf(" -verify\t- turn on peer certificate verification\n"); + printf(" -cert arg\t- certificate file to use - default DER format\n"); + printf(" -key arg\t- Private key file to use - default DER format\n"); + printf("\t\t Can repeat up to %d times\n", $cert_size); + printf(" -CAfile arg\t- Certificate authority - default DER format\n"); + printf("\t\t Can repeat up to %d times\n", $ca_cert_size); + printf(" -quiet\t\t- No client output\n"); + printf(" -pass\t\t- private key file pass phrase source\n"); + printf(" -reconnect\t- Drop and re-make the connection ". + "with the same Session-ID\n"); + + if ($build_mode == $axtlsp::SSL_BUILD_FULL_MODE) + { + printf(" -debug\t\t- Print more output\n"); + printf(" -state\t\t- Show state messages\n"); + printf(" -show-rsa\t- Show RSA state\n"); + } + } + else + { + printf("Change configuration to allow this feature\n"); + } + + exit 1; +} + +# +# Display what cipher we are using +# +sub display_cipher +{ + my ($ssl) = @_; + printf("CIPHER is "); + my $cipher_id = axtlsp::ssl_get_cipher_id($ssl); + + if ($cipher_id == $axtlsp::SSL_AES128_SHA) + { + printf("AES128-SHA"); + } + elsif ($cipher_id == $axtlsp::SSL_AES256_SHA) + { + printf("AES256-SHA"); + } + elsif ($axtlsp::SSL_RC4_128_SHA) + { + printf("RC4-SHA"); + } + elsif ($axtlsp::SSL_RC4_128_MD5) + { + printf("RC4-MD5"); + } + else + { + printf("Unknown - %d", $cipher_id); + } + + printf("\n"); +} + +# +# Display what session id we have. +# +sub display_session_id +{ + my ($ssl) = @_; + my $session_id = axtlsp::ssl_get_session_id($ssl); + if (length($$session_id) > 0) + { + printf("-----BEGIN SSL SESSION PARAMETERS-----\n"); + printf(unpack("H*", $$session_id)); + printf("\n-----END SSL SESSION PARAMETERS-----\n"); + } +} diff --git a/libs/nixio/axTLS/samples/vbnet/Makefile b/libs/nixio/axTLS/samples/vbnet/Makefile new file mode 100644 index 000000000..0984d4e02 --- /dev/null +++ b/libs/nixio/axTLS/samples/vbnet/Makefile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +include ../../config/.config +include ../../config/makefile.conf +include ../../config/makefile.dotnet.conf + +# only build on Win32 platforms +ifdef GO_DOT_NET +all : sample +TARGET=../../$(STAGE)/axssl.vbnet.exe +sample : $(TARGET) + +$(TARGET): ../../bindings/vbnet/axTLSvb.vb ../../bindings/vbnet/axInterface.vb axssl.vb + vbc.exe /r:"`cygpath -w "$(CONFIG_DOT_NET_FRAMEWORK_BASE)/System.dll"`" /nologo /t:exe /out:"`cygpath -w $@`" $(foreach file, $^, "`cygpath -w $(file)`") + +endif # ARCH + +clean:: + -@rm -f $(TARGET) + diff --git a/libs/nixio/axTLS/samples/vbnet/axssl.vb b/libs/nixio/axTLS/samples/vbnet/axssl.vb new file mode 100644 index 000000000..1b423c865 --- /dev/null +++ b/libs/nixio/axTLS/samples/vbnet/axssl.vb @@ -0,0 +1,702 @@ +' +' Copyright (c) 2007, Cameron Rich +' +' All rights reserved. +' +' Redistribution and use in source and binary forms, with or without +' modification, are permitted provided that the following conditions are met: +' +' * Redistributions of source code must retain the above copyright notice, +' this list of conditions and the following disclaimer. +' * 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. +' * Neither the name of the axTLS project nor the names of its +' contributors may be used to endorse or promote products derived +' from this software without specific prior written permission. +' +' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +' "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 THE COPYRIGHT OWNER 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. +' + +' +' Demonstrate the use of the axTLS library in VB.NET with a set of +' command-line parameters similar to openssl. In fact, openssl clients +' should be able to communicate with axTLS servers and visa-versa. +' +' This code has various bits enabled depending on the configuration. To enable +' the most interesting version, compile with the 'full mode' enabled. +' +' To see what options you have, run the following: +' > axssl.vbnet.exe s_server -? +' > axssl.vbnet.exe s_client -? +' +' The axtls shared library must be in the same directory or be found +' by the OS. +' + +Imports System +Imports System.Net +Imports System.Net.Sockets +Imports Microsoft.VisualBasic +Imports axTLSvb + +Public Class axssl + ' + ' do_server() + ' + Public Sub do_server(ByVal build_mode As Integer, _ + ByVal args() As String) + Dim i As Integer = 1 + Dim port As Integer = 4433 + Dim options As Integer = axtls.SSL_DISPLAY_CERTS + Dim quiet As Boolean = False + Dim password As String = Nothing + Dim private_key_file As String = Nothing + + ' organise the cert/ca_cert lists + Dim cert_size As Integer = SSLUtil.MaxCerts() + Dim ca_cert_size As Integer = SSLUtil.MaxCACerts() + Dim cert(cert_size) As String + Dim ca_cert(ca_cert_size) As String + Dim cert_index As Integer = 0 + Dim ca_cert_index As Integer = 0 + + While i < args.Length + If args(i) = "-accept" Then + If i >= args.Length-1 + print_server_options(build_mode, args(i)) + End If + + i += 1 + port = Int32.Parse(args(i)) + ElseIf args(i) = "-quiet" + quiet = True + options = options And Not axtls.SSL_DISPLAY_CERTS + ElseIf build_mode >= axtls.SSL_BUILD_SERVER_ONLY + If args(i) = "-cert" + If i >= args.Length-1 Or cert_index >= cert_size + print_server_options(build_mode, args(i)) + End If + + i += 1 + cert(cert_index) = args(i) + cert_index += 1 + ElseIf args(i) = "-key" + If i >= args.Length-1 + print_server_options(build_mode, args(i)) + End If + + i += 1 + private_key_file = args(i) + options = options Or axtls.SSL_NO_DEFAULT_KEY + ElseIf args(i) = "-pass" + If i >= args.Length-1 + print_server_options(build_mode, args(i)) + End If + + i += 1 + password = args(i) + ElseIf build_mode >= axtls.SSL_BUILD_ENABLE_VERIFICATION + If args(i) = "-verify" Then + options = options Or axtls.SSL_CLIENT_AUTHENTICATION + ElseIf args(i) = "-CAfile" + If i >= args.Length-1 Or _ + ca_cert_index >= ca_cert_size Then + print_server_options(build_mode, args(i)) + End If + + i += 1 + ca_cert(ca_cert_index) = args(i) + ca_cert_index += 1 + ElseIf build_mode = axtls.SSL_BUILD_FULL_MODE + If args(i) = "-debug" Then + options = options Or axtls.SSL_DISPLAY_BYTES + ElseIf args(i) = "-state" + options = options Or axtls.SSL_DISPLAY_STATES + ElseIf args(i) = "-show-rsa" + options = options Or axtls.SSL_DISPLAY_RSA + Else + print_server_options(build_mode, args(i)) + End If + Else + print_server_options(build_mode, args(i)) + End If + Else + print_server_options(build_mode, args(i)) + End If + End If + + i += 1 + End While + + ' Create socket for incoming connections + Dim ep As IPEndPoint = New IPEndPoint(IPAddress.Any, port) + Dim server_sock As TcpListener = New TcpListener(ep) + server_sock.Start() + + '********************************************************************* + ' This is where the interesting stuff happens. Up until now we've + ' just been setting up sockets etc. Now we do the SSL handshake. + '*********************************************************************/ + Dim ssl_ctx As SSLServer = New SSLServer(options, _ + axtls.SSL_DEFAULT_SVR_SESS) + + If ssl_ctx Is Nothing Then + Console.Error.WriteLine("Error: Server context is invalid") + Environment.Exit(1) + End If + + If private_key_file <> Nothing Then + Dim obj_type As Integer = axtls.SSL_OBJ_RSA_KEY + + If private_key_file.EndsWith(".p8") Then + obj_type = axtls.SSL_OBJ_PKCS8 + Else If (private_key_file.EndsWith(".p12")) + obj_type = axtls.SSL_OBJ_PKCS12 + End If + + If ssl_ctx.ObjLoad(obj_type, private_key_file, _ + password) <> axtls.SSL_OK Then + Console.Error.WriteLine("Error: Private key '" & _ + private_key_file & "' is undefined.") + Environment.Exit(1) + End If + End If + + For i = 0 To cert_index-1 + If ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CERT, _ + cert(i), Nothing) <> axtls.SSL_OK Then + Console.WriteLine("Certificate '" & cert(i) & _ + "' is undefined.") + Environment.Exit(1) + End If + Next + + For i = 0 To ca_cert_index-1 + If ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CACERT, _ + ca_cert(i), Nothing) <> axtls.SSL_OK Then + Console.WriteLine("Certificate '" & ca_cert(i) & _ + "' is undefined.") + Environment.Exit(1) + End If + Next + + Dim buf As Byte() = Nothing + Dim res As Integer + Dim ssl As SSL + + While 1 + If Not quiet Then + Console.WriteLine("ACCEPT") + End If + + Dim client_sock As Socket = server_sock.AcceptSocket() + + ssl = ssl_ctx.Connect(client_sock) + + ' do the actual SSL handshake + While 1 + res = ssl_ctx.Read(ssl, buf) + If res <> axtls.SSL_OK Then + Exit While + End If + + ' check when the connection has been established + If ssl.HandshakeStatus() = axtls.SSL_OK + Exit While + End If + + ' could do something else here + End While + + If res = axtls.SSL_OK Then ' connection established and ok + If Not quiet + display_session_id(ssl) + display_cipher(ssl) + End If + + ' now read (and display) whatever the client sends us + While 1 + ' keep reading until we get something interesting + While 1 + res = ssl_ctx.Read(ssl, buf) + If res <> axtls.SSL_OK Then + Exit While + End If + + ' could do something else here + End While + + If res < axtls.SSL_OK + If Not quiet + Console.WriteLine("CONNECTION CLOSED") + End If + + Exit While + End If + + ' convert to String + Dim str(res) As Char + For i = 0 To res-1 + str(i) = Chr(buf(i)) + Next + + Console.Write(str) + End While + ElseIf Not quiet + SSLUtil.DisplayError(res) + End If + + ' client was disconnected or the handshake failed. */ + ssl.Dispose() + client_sock.Close() + End While + + ssl_ctx.Dispose() + End Sub + + ' + ' do_client() + ' + Public Sub do_client(ByVal build_mode As Integer, _ + ByVal args() As String) + + If build_mode < axtls.SSL_BUILD_ENABLE_CLIENT Then + print_client_options(build_mode, args(1)) + End If + + Dim i As Integer = 1 + Dim res As Integer + Dim port As Integer = 4433 + Dim quiet As Boolean = False + Dim password As String = Nothing + Dim reconnect As Integer = 0 + Dim private_key_file As String = Nothing + Dim hostname As String = "127.0.0.1" + + ' organise the cert/ca_cert lists + Dim ssl As SSL = Nothing + Dim cert_size As Integer = SSLUtil.MaxCerts() + Dim ca_cert_size As Integer = SSLUtil.MaxCACerts() + Dim cert(cert_size) As String + Dim ca_cert(ca_cert_size) As String + Dim cert_index As Integer = 0 + Dim ca_cert_index As Integer = 0 + + Dim options As Integer = _ + axtls.SSL_SERVER_VERIFY_LATER Or axtls.SSL_DISPLAY_CERTS + Dim session_id As Byte() = Nothing + + While i < args.Length + If args(i) = "-connect" Then + Dim host_port As String + + If i >= args.Length-1 + print_client_options(build_mode, args(i)) + End If + + i += 1 + host_port = args(i) + + Dim index_colon As Integer = host_port.IndexOf(":"C) + If index_colon < 0 Then + print_client_options(build_mode, args(i)) + End If + + hostname = New String(host_port.ToCharArray(), _ + 0, index_colon) + port = Int32.Parse(New String(host_port.ToCharArray(), _ + index_colon+1, host_port.Length-index_colon-1)) + ElseIf args(i) = "-cert" + If i >= args.Length-1 Or cert_index >= cert_size Then + print_client_options(build_mode, args(i)) + End If + + i += 1 + cert(cert_index) = args(i) + cert_index += 1 + ElseIf args(i) = "-key" + If i >= args.Length-1 + print_client_options(build_mode, args(i)) + End If + + i += 1 + private_key_file = args(i) + options = options Or axtls.SSL_NO_DEFAULT_KEY + ElseIf args(i) = "-CAfile" + If i >= args.Length-1 Or ca_cert_index >= ca_cert_size + print_client_options(build_mode, args(i)) + End If + + i += 1 + ca_cert(ca_cert_index) = args(i) + ca_cert_index += 1 + ElseIf args(i) = "-verify" + options = options And Not axtls.SSL_SERVER_VERIFY_LATER + ElseIf args(i) = "-reconnect" + reconnect = 4 + ElseIf args(i) = "-quiet" + quiet = True + options = options And Not axtls.SSL_DISPLAY_CERTS + ElseIf args(i) = "-pass" + If i >= args.Length-1 + print_client_options(build_mode, args(i)) + End If + + i += 1 + password = args(i) + ElseIf build_mode = axtls.SSL_BUILD_FULL_MODE + If args(i) = "-debug" Then + options = options Or axtls.SSL_DISPLAY_BYTES + ElseIf args(i) = "-state" + options = options Or axtls.SSL_DISPLAY_STATES + ElseIf args(i) = "-show-rsa" + options = options Or axtls.SSL_DISPLAY_RSA + Else + print_client_options(build_mode, args(i)) + End If + Else ' don't know what this is + print_client_options(build_mode, args(i)) + End If + + i += 1 + End While + + 'Dim hostInfo As IPHostEntry = Dns.Resolve(hostname) + Dim hostInfo As IPHostEntry = Dns.GetHostEntry(hostname) + Dim addresses As IPAddress() = hostInfo.AddressList + Dim ep As IPEndPoint = New IPEndPoint(addresses(0), port) + Dim client_sock As Socket = New Socket(AddressFamily.InterNetwork, _ + SocketType.Stream, ProtocolType.Tcp) + client_sock.Connect(ep) + + If Not client_sock.Connected Then + Console.WriteLine("could not connect") + Environment.Exit(1) + End If + + If Not quiet Then + Console.WriteLine("CONNECTED") + End If + + '********************************************************************* + ' This is where the interesting stuff happens. Up until now we've + ' just been setting up sockets etc. Now we do the SSL handshake. + '*********************************************************************/ + Dim ssl_ctx As SSLClient = New SSLClient(options, _ + axtls.SSL_DEFAULT_CLNT_SESS) + + If ssl_ctx Is Nothing Then + Console.Error.WriteLine("Error: Client context is invalid") + Environment.Exit(1) + End If + + If private_key_file <> Nothing Then + Dim obj_type As Integer = axtls.SSL_OBJ_RSA_KEY + + If private_key_file.EndsWith(".p8") Then + obj_type = axtls.SSL_OBJ_PKCS8 + Else If (private_key_file.EndsWith(".p12")) + obj_type = axtls.SSL_OBJ_PKCS12 + End If + + If ssl_ctx.ObjLoad(obj_type, private_key_file, _ + password) <> axtls.SSL_OK Then + Console.Error.WriteLine("Error: Private key '" & _ + private_key_file & "' is undefined.") + Environment.Exit(1) + End If + End If + + For i = 0 To cert_index-1 + If ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CERT, _ + cert(i), Nothing) <> axtls.SSL_OK Then + Console.WriteLine("Certificate '" & cert(i) & _ + "' is undefined.") + Environment.Exit(1) + End If + Next + + For i = 0 To ca_cert_index-1 + If ssl_ctx.ObjLoad(axtls.SSL_OBJ_X509_CACERT, _ + ca_cert(i), Nothing) <> axtls.SSL_OK Then + Console.WriteLine("Certificate '" & ca_cert(i) & _ + "' is undefined.") + Environment.Exit(1) + End If + Next + + ' Try session resumption? + If reconnect > 0 Then + While reconnect > 0 + reconnect -= 1 + ssl = ssl_ctx.Connect(client_sock, session_id) + + res = ssl.HandshakeStatus() + If res <> axtls.SSL_OK Then + If Not quiet Then + SSLUtil.DisplayError(res) + End If + + ssl.Dispose() + Environment.Exit(1) + End If + + display_session_id(ssl) + session_id = ssl.GetSessionId() + + If reconnect > 0 Then + ssl.Dispose() + client_sock.Close() + + ' and reconnect + client_sock = New Socket(AddressFamily.InterNetwork, _ + SocketType.Stream, ProtocolType.Tcp) + client_sock.Connect(ep) + End If + End While + Else + ssl = ssl_ctx.Connect(client_sock, Nothing) + End If + + ' check the return status + res = ssl.HandshakeStatus() + If res <> axtls.SSL_OK Then + If Not quiet Then + SSLUtil.DisplayError(res) + End If + + Environment.Exit(1) + End If + + If Not quiet Then + Dim common_name As String = _ + ssl.GetCertificateDN(axtls.SSL_X509_CERT_COMMON_NAME) + + If common_name <> Nothing + Console.WriteLine("Common Name:" & _ + ControlChars.Tab & ControlChars.Tab & _ + ControlChars.Tab & common_name) + End If + + display_session_id(ssl) + display_cipher(ssl) + End If + + While (1) + Dim user_input As String = Console.ReadLine() + + If user_input = Nothing Then + Exit While + End If + + Dim buf(user_input.Length+1) As Byte + buf(buf.Length-2) = Asc(ControlChars.Lf) ' add the carriage return + buf(buf.Length-1) = 0 ' null terminate + + For i = 0 To user_input.Length-1 + buf(i) = Asc(user_input.Chars(i)) + Next + + res = ssl_ctx.Write(ssl, buf, buf.Length) + If res < axtls.SSL_OK Then + If Not quiet Then + SSLUtil.DisplayError(res) + End If + + Exit While + End If + End While + + ssl_ctx.Dispose() + End Sub + + ' + ' Display what cipher we are using + ' + Private Sub display_cipher(ByVal ssl As SSL) + Console.Write("CIPHER is ") + + Select ssl.GetCipherId() + Case axtls.SSL_AES128_SHA + Console.WriteLine("AES128-SHA") + + Case axtls.SSL_AES256_SHA + Console.WriteLine("AES256-SHA") + + Case axtls.SSL_RC4_128_SHA + Console.WriteLine("RC4-SHA") + + Case axtls.SSL_RC4_128_MD5 + Console.WriteLine("RC4-MD5") + + Case Else + Console.WriteLine("Unknown - " & ssl.GetCipherId()) + End Select + End Sub + + ' + ' Display what session id we have. + ' + Private Sub display_session_id(ByVal ssl As SSL) + Dim session_id As Byte() = ssl.GetSessionId() + + If session_id.Length > 0 Then + Console.WriteLine("-----BEGIN SSL SESSION PARAMETERS-----") + Dim b As Byte + For Each b In session_id + Console.Write("{0:x02}", b) + Next + + Console.WriteLine() + Console.WriteLine("-----END SSL SESSION PARAMETERS-----") + End If + End Sub + + ' + ' We've had some sort of command-line error. Print out the basic options. + ' + Public Sub print_options(ByVal options As String) + Console.WriteLine("axssl: Error: '" & options & _ + "' is an invalid command.") + Console.WriteLine("usage: axssl.vbnet [s_server|s_client|" & _ + "version] [args ...]") + Environment.Exit(1) + End Sub + + ' + ' We've had some sort of command-line error. Print out the server options. + ' + Private Sub print_server_options(ByVal build_mode As Integer, _ + ByVal options As String) + Dim cert_size As Integer = SSLUtil.MaxCerts() + Dim ca_cert_size As Integer = SSLUtil.MaxCACerts() + + Console.WriteLine("unknown option " & options) + Console.WriteLine("usage: s_server [args ...]") + Console.WriteLine(" -accept arg" & ControlChars.Tab & _ + "- port to accept on (default is 4433)") + Console.WriteLine(" -quiet" & ControlChars.Tab & ControlChars.Tab & _ + "- No server output") + If build_mode >= axtls.SSL_BUILD_SERVER_ONLY + Console.WriteLine(" -cert arg" & ControlChars.Tab & _ + "- certificate file to add (in addition to default) to chain -") + Console.WriteLine(ControlChars.Tab & ControlChars.Tab & _ + " Can repeat up to " & cert_size & " times") + Console.WriteLine(" -key arg" & ControlChars.Tab & _ + "- Private key file to use") + Console.WriteLine(" -pass" & ControlChars.Tab & ControlChars.Tab & _ + "- private key file pass phrase source") + End If + + If build_mode >= axtls.SSL_BUILD_ENABLE_VERIFICATION + Console.WriteLine(" -verify" & ControlChars.Tab & _ + "- turn on peer certificate verification") + Console.WriteLine(" -CAfile arg" & ControlChars.Tab & _ + "- Certificate authority") + Console.WriteLine(ControlChars.Tab & ControlChars.Tab & _ + " Can repeat up to " & ca_cert_size & " times") + End If + + If build_mode = axtls.SSL_BUILD_FULL_MODE + Console.WriteLine(" -debug" & _ + ControlChars.Tab & ControlChars.Tab & _ + "- Print more output") + Console.WriteLine(" -state" & _ + ControlChars.Tab & ControlChars.Tab & _ + "- Show state messages") + Console.WriteLine(" -show-rsa" & _ + ControlChars.Tab & "- Show RSA state") + End If + + Environment.Exit(1) + End Sub + + ' + ' We've had some sort of command-line error. Print out the client options. + ' + Private Sub print_client_options(ByVal build_mode As Integer, _ + ByVal options As String) + Dim cert_size As Integer = SSLUtil.MaxCerts() + Dim ca_cert_size As Integer = SSLUtil.MaxCACerts() + + Console.WriteLine("unknown option " & options) + + If build_mode >= axtls.SSL_BUILD_ENABLE_CLIENT Then + Console.WriteLine("usage: s_client [args ...]") + Console.WriteLine(" -connect host:port - who to connect to " & _ + "(default is localhost:4433)") + Console.WriteLine(" -verify" & ControlChars.Tab & _ + "- turn on peer certificate verification") + Console.WriteLine(" -cert arg" & ControlChars.Tab & _ + "- certificate file to use") + Console.WriteLine(ControlChars.Tab & ControlChars.Tab & _ + " Can repeat up to " & cert_size & " times") + Console.WriteLine(" -key arg" & ControlChars.Tab & _ + "- Private key file to use") + Console.WriteLine(" -CAfile arg" & ControlChars.Tab & _ + "- Certificate authority") + Console.WriteLine(ControlChars.Tab & ControlChars.Tab & _ + " Can repeat up to " & ca_cert_size & " times") + Console.WriteLine(" -quiet" & _ + ControlChars.Tab & ControlChars.Tab & "- No client output") + Console.WriteLine(" -pass" & ControlChars.Tab & _ + ControlChars.Tab & _ + "- private key file pass phrase source") + Console.WriteLine(" -reconnect" & ControlChars.Tab & _ + "- Drop and re-make the " & _ + "connection with the same Session-ID") + + If build_mode = axtls.SSL_BUILD_FULL_MODE Then + Console.WriteLine(" -debug" & _ + ControlChars.Tab & ControlChars.Tab & _ + "- Print more output") + Console.WriteLine(" -state" & _ + ControlChars.Tab & ControlChars.Tab & _ + "- Show state messages") + Console.WriteLine(" -show-rsa" & ControlChars.Tab & _ + "- Show RSA state") + End If + Else + Console.WriteLine("Change configuration to allow this feature") + End If + + Environment.Exit(1) + End Sub + +End Class + +Public Module MyMain + Function Main(ByVal args() As String) As Integer + Dim runner As axssl = New axssl() + + If args.Length = 1 And args(0) = "version" Then + Console.WriteLine("axssl.vbnet " & SSLUtil.Version()) + Environment.Exit(0) + End If + + If args.Length < 1 + runner.print_options("") + ElseIf args(0) <> "s_server" And args(0) <> "s_client" + runner.print_options(args(0)) + End If + + Dim build_mode As Integer = SSLUtil.BuildMode() + + If args(0) = "s_server" Then + runner.do_server(build_mode, args) + Else + runner.do_client(build_mode, args) + End If + End Function +End Module diff --git a/libs/nixio/axTLS/ssl/.depend b/libs/nixio/axTLS/ssl/.depend new file mode 100644 index 000000000..85f138fea --- /dev/null +++ b/libs/nixio/axTLS/ssl/.depend @@ -0,0 +1,31 @@ +asn1.o: asn1.c os_port.h ../crypto/crypto.h ../config/config.h \ + ../ssl/os_port.h ../crypto/bigint_impl.h ../crypto/bigint.h \ + ../crypto/crypto.h crypto_misc.h ../crypto/bigint.h +gen_cert.o: gen_cert.c ../config/config.h ssl.h tls1.h version.h \ + ../crypto/crypto.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h os_port.h crypto_misc.h \ + ../crypto/bigint.h +loader.o: loader.c ssl.h tls1.h version.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h os_port.h crypto_misc.h \ + ../crypto/bigint.h private_key.h +openssl.o: openssl.c ../config/config.h +os_port.o: os_port.c os_port.h +p12.o: p12.c ssl.h tls1.h version.h ../crypto/crypto.h ../config/config.h \ + ../ssl/os_port.h ../crypto/bigint_impl.h ../crypto/bigint.h \ + ../crypto/crypto.h os_port.h crypto_misc.h ../crypto/bigint.h +tls1.o: tls1.c ssl.h tls1.h version.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h os_port.h crypto_misc.h \ + ../crypto/bigint.h +tls1_clnt.o: tls1_clnt.c ssl.h tls1.h version.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h os_port.h crypto_misc.h \ + ../crypto/bigint.h +tls1_svr.o: tls1_svr.c ssl.h tls1.h version.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h os_port.h crypto_misc.h \ + ../crypto/bigint.h +x509.o: x509.c os_port.h crypto_misc.h ../crypto/crypto.h \ + ../config/config.h ../ssl/os_port.h ../crypto/bigint_impl.h \ + ../crypto/bigint.h ../crypto/crypto.h ../crypto/bigint.h diff --git a/libs/nixio/axTLS/ssl/BigIntConfig.in b/libs/nixio/axTLS/ssl/BigIntConfig.in new file mode 100644 index 000000000..04c7438c0 --- /dev/null +++ b/libs/nixio/axTLS/ssl/BigIntConfig.in @@ -0,0 +1,132 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/config/Kconfig-language.txt +# + +menu "BigInt Options" + depends on !CONFIG_SSL_SKELETON_MODE + +choice + prompt "Reduction Algorithm" + default CONFIG_BIGINT_BARRETT + +config CONFIG_BIGINT_CLASSICAL + bool "Classical" + help + Classical uses standard division. It has no limitations and is + theoretically the slowest due to the divisions used. For this particular + implementation it is surprisingly quite fast. + +config CONFIG_BIGINT_MONTGOMERY + bool "Montgomery" + help + Montgomery uses simple addition and multiplication to achieve its + performance. In this implementation it is slower than classical, + and it has the limitation that 0 <= x, y < m, and so is not used + when CRT is active. + + This option will not be normally selected. + +config CONFIG_BIGINT_BARRETT + bool "Barrett" + help + Barrett performs expensive precomputation before reduction and partial + multiplies for computational speed. It can't be used with some of the + calculations when CRT is used, and so defaults to classical when this + occurs. + + It is about 40% faster than Classical/Montgomery with the expense of + about 2kB, and so this option is normally selected. + +endchoice + +config CONFIG_BIGINT_CRT + bool "Chinese Remainder Theorem (CRT)" + default y + help + Allow the Chinese Remainder Theorem (CRT) to be used. + + Uses a number of extra coefficients from the private key to improve the + performance of a decryption. This feature is one of the most + significant performance improvements (it reduces a decryption time by + over 3 times). + + This option should be selected. + +config CONFIG_BIGINT_KARATSUBA + bool "Karatsuba Multiplication" + default n + help + Allow Karasuba multiplication to be used. + + Uses 3 multiplications (plus a number of additions/subtractions) + instead of 4. Multiplications are O(N^2) but addition/subtraction + is O(N) hence for large numbers is beneficial. For this project, the + effect was only useful for 4096 bit keys. As these aren't likely to + be used, the feature is disabled by default. + + It costs about 2kB to enable it. + +config MUL_KARATSUBA_THRESH + int "Karatsuba Multiplication Theshold" + default 20 + depends on CONFIG_BIGINT_KARATSUBA + help + The minimum number of components needed before Karasuba muliplication + is used. + + This is very dependent on the speed/implementation of bi_add()/ + bi_subtract(). There is a bit of trial and error here and will be + at a different point for different architectures. + +config SQU_KARATSUBA_THRESH + int "Karatsuba Square Threshold" + default 40 + depends on CONFIG_BIGINT_KARATSUBA && CONFIG_BIGINT_SQUARE + help + The minimum number of components needed before Karatsuba squaring + is used. + + This is very dependent on the speed/implementation of bi_add()/ + bi_subtract(). There is a bit of trial and error here and will be + at a different point for different architectures. + +config CONFIG_BIGINT_SLIDING_WINDOW + bool "Sliding Window Exponentiation" + default y + help + Allow Sliding-Window Exponentiation to be used. + + Potentially processes more than 1 bit at a time when doing + exponentiation. The sliding-window technique reduces the number of + precomputations compared to other precomputed techniques. + + It results in a considerable performance improvement with it enabled + (it halves the decryption time) and so should be selected. + +config CONFIG_BIGINT_SQUARE + bool "Square Algorithm" + default y + help + Allow squaring to be used instead of a multiplication. + + Squaring is theoretically 50% faster than a standard multiply + (but is actually about 25% faster). + + It gives a 20% speed improvement and so should be selected. + +config CONFIG_BIGINT_CHECK_ON + bool "BigInt Integrity Checking" + default n if !CONFIG_DEBUG + default y if CONFIG_DEBUG + help + This is used when developing bigint algorithms. It performs a sanity + check on all operations at the expense of speed. + + This option is only selected when developing and should normally be + turned off. + +endmenu + + + diff --git a/libs/nixio/axTLS/ssl/Config.in b/libs/nixio/axTLS/ssl/Config.in new file mode 100644 index 000000000..d047d420e --- /dev/null +++ b/libs/nixio/axTLS/ssl/Config.in @@ -0,0 +1,336 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/config/Kconfig-language.txt +# + +menu "SSL Library" + +choice + prompt "Mode" + default CONFIG_SSL_FULL_MODE + +config CONFIG_SSL_SERVER_ONLY + bool "Server only - no verification" + help + Enable server functionality (no client functionality). + This mode still supports sessions and chaining (which can be turned + off in configuration). + + The axssl sample runs with the minimum of features. + + This is the most space efficient of the modes with the library + about 45kB in size. Use this mode if you are doing standard SSL server + work. + +config CONFIG_SSL_CERT_VERIFICATION + bool "Server only - with verification" + help + Enable server functionality with client authentication (no client + functionality). + + The axssl sample runs with the "-verify" and "-CAfile" options. + + This mode produces a library about 49kB in size. Use this mode if you + have an SSL server which requires client authentication (which is + uncommon in browser applications). + +config CONFIG_SSL_ENABLE_CLIENT + bool "Client/Server enabled" + help + Enable client/server functionality (including peer authentication). + + The axssl sample runs with the "s_client" option enabled. + + This mode produces a library about 51kB in size. Use this mode if you + require axTLS to use SSL client functionality (the SSL server code + is always enabled). + +config CONFIG_SSL_FULL_MODE + bool "Client/Server enabled with diagnostics" + help + Enable client/server functionality including diagnostics. Most of the + extra size in this mode is due to the storage of various strings that + are used. + + The axssl sample has 3 more options, "-debug", "-state" and "-show-rsa" + + This mode produces a library about 58kB in size. It is suggested that + this mode is used only during development, or systems that have more + generous memory limits. + + It is the default to demonstrate the features of axTLS. + +config CONFIG_SSL_SKELETON_MODE + bool "Skeleton mode - the smallest server mode" + help + This is an experiment to build the smallest library at the expense of + features and speed. + + * Server mode only. + * The AES cipher is disabled. + * No session resumption. + * No external keys/certificates are supported. + * The bigint library has most of the performance features disabled. + * Some other features/API calls may not work. + + This mode produces a library about 37kB in size. The main + disadvantage of this mode is speed - it will be much slower than the + other build modes. + +endchoice + +choice + prompt "Protocol Preference" + depends on !CONFIG_SSL_SKELETON_MODE + default CONFIG_SSL_PROT_MEDIUM + +config CONFIG_SSL_PROT_LOW + bool "Low" + help + Chooses the cipher in the order of RC4-SHA, AES128-SHA, AES256-SHA. + + This will use the fastest cipher(s) but at the expense of security. + +config CONFIG_SSL_PROT_MEDIUM + bool "Medium" + help + Chooses the cipher in the order of AES128-SHA, AES256-SHA, RC4-SHA. + + This mode is a balance between speed and security and is the default. + +config CONFIG_SSL_PROT_HIGH + bool "High" + help + Chooses the cipher in the order of AES256-SHA, AES128-SHA, RC4-SHA. + + This will use the strongest cipher(s) at the cost of speed. + +endchoice + +config CONFIG_SSL_USE_DEFAULT_KEY + bool "Enable default key" + depends on !CONFIG_SSL_SKELETON_MODE + default y + help + Some applications will not require the default private key/certificate + that is built in. This is one way to save on a couple of kB's if an + external private key/certificate is used. + + The private key is in ssl/private_key.h and the certificate is in + ssl/cert.h. + + The advantage of a built-in private key/certificate is that no file + system is required for access. Both the certificate and the private + key will be automatically loaded on a ssl_ctx_new(). + + However this private key/certificate can never be changed (without a + code update). + + This mode is enabled by default. Disable this mode if the + built-in key/certificate is not used. + +config CONFIG_SSL_PRIVATE_KEY_LOCATION + string "Private key file location" + depends on !CONFIG_SSL_USE_DEFAULT_KEY && !CONFIG_SSL_SKELETON_MODE + help + The file location of the private key which will be automatically + loaded on a ssl_ctx_new(). + +config CONFIG_SSL_PRIVATE_KEY_PASSWORD + string "Private key password" + depends on !CONFIG_SSL_USE_DEFAULT_KEY && CONFIG_SSL_HAS_PEM + help + The password required to decrypt a PEM-encoded password file. + +config CONFIG_SSL_X509_CERT_LOCATION + string "X.509 certificate file location" + depends on !CONFIG_SSL_GENERATE_X509_CERT && !CONFIG_SSL_USE_DEFAULT_KEY && !CONFIG_SSL_SKELETON_MODE + help + The file location of the X.509 certificate which will be automatically + loaded on a ssl_ctx_new(). + +config CONFIG_SSL_GENERATE_X509_CERT + bool "Generate X.509 Certificate" + default n + help + An X.509 certificate can be automatically generated on a + ssl_ctx_new(). A private key still needs to be provided (the private + key in ss/private_key.h will be used unless + CONFIG_SSL_PRIVATE_KEY_LOCATION is set). + + The certificate is generated on the fly, and so a minor start-up time + penalty is to be expected. This feature adds around 5kB to the + library. + + This feature is disabled by default. + +config CONFIG_SSL_X509_COMMON_NAME + string "X.509 Common Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The common name for the X.509 certificate. This should be the fully + qualified domain name (FQDN), e.g. www.foo.com. + + If this is blank, then this will be value from gethostname() and + getdomainname(). + +config CONFIG_SSL_X509_ORGANIZATION_NAME + string "X.509 Organization Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The organization name for the generated X.509 certificate. + + This field is optional. + +config CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + string "X.509 Organization Unit Name" + depends on CONFIG_SSL_GENERATE_X509_CERT + help + The organization unit name for the generated X.509 certificate. + + This field is optional. + +config CONFIG_SSL_ENABLE_V23_HANDSHAKE + bool "Enable v23 Handshake" + default y + help + Some browsers use the v23 handshake client hello message + (an SSL2 format message which all SSL servers can understand). + It may be used if SSL2 is enabled in the browser. + + Since this feature takes a kB or so, this feature may be disabled - at + the risk of making it incompatible with some browsers (IE6 is ok, + Firefox 1.5 and below use it). + + Disable if backwards compatibility is not an issue (i.e. the client is + always using TLS1.0) + +config CONFIG_SSL_HAS_PEM + bool "Enable PEM" + default n if !CONFIG_SSL_FULL_MODE + default y if CONFIG_SSL_FULL_MODE + depends on !CONFIG_SSL_SKELETON_MODE + help + Enable the use of PEM format for certificates and private keys. + + PEM is not normally needed - PEM files can be converted into DER files + quite easily. However they have the convenience of allowing multiple + certificates/keys in the same file. + + This feature will add a couple of kB to the library. + + Disable if PEM is not used (which will be in most cases). + +config CONFIG_SSL_USE_PKCS12 + bool "Use PKCS8/PKCS12" + default n if !CONFIG_SSL_FULL_MODE + default y if CONFIG_SSL_FULL_MODE + depends on !CONFIG_SSL_SERVER_ONLY && !CONFIG_SSL_SKELETON_MODE + help + PKCS#12 certificates combine private keys and certificates together in + one file. + + PKCS#8 private keys are also suppported (as it is a subset of PKCS#12). + + The decryption of these certificates uses RC4-128 (and these + certificates must be encrypted using this cipher). The actual + algorithm is "PBE-SHA1-RC4-128". + + Disable if PKCS#12 is not used (which will be in most cases). + +config CONFIG_SSL_EXPIRY_TIME + int "Session expiry time (in hours)" + depends on !CONFIG_SSL_SKELETON_MODE + default 24 + help + The time (in hours) before a session expires. + + A longer time means that the expensive parts of a handshake don't + need to be run when a client reconnects later. + + The default is 1 day. + +config CONFIG_X509_MAX_CA_CERTS + int "Maximum number of certificate authorites" + default 4 + depends on !CONFIG_SSL_SERVER_ONLY && !CONFIG_SSL_SKELETON_MODE + help + Determines the number of CA's allowed. + + Increase this figure if more trusted sites are allowed. Each + certificate adds about 300 bytes (when added). + + The default is to allow four certification authorities. + +config CONFIG_SSL_MAX_CERTS + int "Maximum number of chained certificates" + default 2 + help + Determines the number of certificates used in a certificate + chain. The chain length must be at least 1. + + Increase this figure if more certificates are to be added to the + chain. Each certificate adds about 300 bytes (when added). + + The default is to allow one certificate + 1 certificate in the chain + (which may be the certificate authority certificate). + +config CONFIG_SSL_CTX_MUTEXING + bool "Enable SSL_CTX mutexing" + default n + help + Normally mutexing is not required - each SSL_CTX object can deal with + many SSL objects (as long as each SSL_CTX object is using a single + thread). + + If the SSL_CTX object is not thread safe e.g. the case where a + new thread is created for each SSL object, then mutexing is required. + + Select y when a mutex on the SSL_CTX object is required. + +config CONFIG_USE_DEV_URANDOM + bool "Use /dev/urandom" + default y + depends on !CONFIG_PLATFORM_WIN32 + help + Use /dev/urandom. Otherwise a custom RNG is used. + + This will be the default on most Linux systems. + +config CONFIG_WIN32_USE_CRYPTO_LIB + bool "Use Win32 Crypto Library" + depends on CONFIG_PLATFORM_WIN32 + help + Microsoft produce a Crypto API which requires the Platform SDK to be + installed. It's used for the RNG. + + This will be the default on most Win32 systems. + +config CONFIG_OPENSSL_COMPATIBLE + bool "Enable openssl API compatibility" + default n + help + To ease the porting of openssl applications, a subset of the openssl + API is wrapped around the axTLS API. + + Note: not all the API is implemented, so parts may still break. And + it's definitely not 100% compatible. + +config CONFIG_PERFORMANCE_TESTING + bool "Build the bigint performance test tool" + default n + help + Used for performance testing of bigint. + + This is a testing tool and is normally disabled. + +config CONFIG_SSL_TEST + bool "Build the SSL testing tool" + default n + depends on CONFIG_SSL_FULL_MODE && !CONFIG_SSL_GENERATE_X509_CERT + help + Used for sanity checking the SSL handshaking. + + This is a testing tool and is normally disabled. + +endmenu diff --git a/libs/nixio/axTLS/ssl/Makefile b/libs/nixio/axTLS/ssl/Makefile new file mode 100644 index 000000000..62a62047d --- /dev/null +++ b/libs/nixio/axTLS/ssl/Makefile @@ -0,0 +1,123 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +AXTLS_HOME=.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +all: libs +ifdef CONFIG_PERFORMANCE_TESTING + $(MAKE) -C test +else +ifdef CONFIG_SSL_TEST + $(MAKE) -C test +endif +endif + +ifndef CONFIG_PLATFORM_WIN32 +TARGET1=$(AXTLS_HOME)/$(STAGE)/libaxtls.a +BASETARGET=libaxtls.so +CRYPTO_PATH=$(AXTLS_HOME)/crypto/ +ifdef CONFIG_PLATFORM_CYGWIN +TARGET2=$(AXTLS_HOME)/$(STAGE)/libaxtls.dll.a +else +TARGET2=$(AXTLS_HOME)/$(STAGE)/$(LIBMINOR) +endif + +# shared library major/minor numbers +LIBMAJOR=$(BASETARGET).1 +LIBMINOR=$(BASETARGET).1.2 +else +TARGET1=$(AXTLS_HOME)/$(STAGE)/axtls.lib +TARGET2=$(AXTLS_HOME)/$(STAGE)/axtls.dll +STATIC_LIB=$(AXTLS_HOME)/$(STAGE)/axtls.static.lib +CRYPTO_PATH=$(AXTLS_HOME)\\crypto\\ +endif + +libs: $(TARGET1) $(TARGET2) + +CRYPTO_OBJ=\ + $(CRYPTO_PATH)aes.o \ + $(CRYPTO_PATH)bigint.o \ + $(CRYPTO_PATH)crypto_misc.o \ + $(CRYPTO_PATH)hmac.o \ + $(CRYPTO_PATH)md2.o \ + $(CRYPTO_PATH)md5.o \ + $(CRYPTO_PATH)rc4.o \ + $(CRYPTO_PATH)rsa.o \ + $(CRYPTO_PATH)sha1.o + +OBJ=\ + asn1.o \ + gen_cert.o \ + loader.o \ + openssl.o \ + os_port.o \ + p12.o \ + tls1.o \ + tls1_svr.o \ + tls1_clnt.o \ + x509.o + +include $(AXTLS_HOME)/config/makefile.post + +ifndef CONFIG_PLATFORM_WIN32 # Linux/Unix/Cygwin + +$(TARGET1) : $(OBJ) + $(AR) -r $@ $(CRYPTO_OBJ) $(OBJ) + +$(TARGET2) : $(OBJ) +ifndef CONFIG_PLATFORM_CYGWIN + $(LD) $(LDFLAGS) $(LDSHARED) -Wl,-soname,$(LIBMAJOR) -o $(AXTLS_HOME)/$(STAGE)/$(LIBMINOR) $(CRYPTO_OBJ) $(OBJ) + cd $(AXTLS_HOME)/$(STAGE); ln -sf $(LIBMINOR) $(LIBMAJOR); ln -sf $(LIBMAJOR) $(BASETARGET); cd - +else + $(LD) $(LDFLAGS) $(LDSHARED) -o $(AXTLS_HOME)/$(STAGE)/cygaxtls.dll \ + -Wl,--out-implib=$(AXTLS_HOME)/$(STAGE)/libaxtls.dll.a \ + -Wl,--export-all-symbols \ + -Wl,--enable-auto-import $(CRYPTO_OBJ) $(OBJ) +endif + +else # Win32 +CRYPTO_OBJ:=$(CRYPTO_OBJ:.o=.obj) + +$(TARGET1) : $(OBJ) + $(AR) /out:$@ $(CRYPTO_OBJ) $(OBJ) + +$(TARGET2) : $(OBJ) + cp $(TARGET1) $(STATIC_LIB) + $(LD) $(LDFLAGS) $(LDSHARED) /out:$@ $(CRYPTO_OBJ) $(OBJ) + +endif + +clean:: + $(MAKE) -C test clean + -@rm -f $(AXTLS_HOME)/$(STAGE)/* *.a $(TARGET1) $(TARGET2) + diff --git a/libs/nixio/axTLS/ssl/asn1.c b/libs/nixio/axTLS/ssl/asn1.c new file mode 100644 index 000000000..4f2e6db24 --- /dev/null +++ b/libs/nixio/axTLS/ssl/asn1.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Some primitive asn methods for extraction ASN.1 data. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto.h" +#include "crypto_misc.h" + +#define SIG_OID_PREFIX_SIZE 8 +#define SIG_IIS6_OID_SIZE 5 + +/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */ +static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = +{ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 +}; + +static const uint8_t sig_iis6_oid[SIG_IIS6_OID_SIZE] = +{ + 0x2b, 0x0e, 0x03, 0x02, 0x1d +}; + +/* CN, O, OU */ +static const uint8_t g_dn_types[] = { 3, 10, 11 }; + +int get_asn1_length(const uint8_t *buf, int *offset) +{ + int len, i; + + if (!(buf[*offset] & 0x80)) /* short form */ + { + len = buf[(*offset)++]; + } + else /* long form */ + { + int length_bytes = buf[(*offset)++]&0x7f; + len = 0; + for (i = 0; i < length_bytes; i++) + { + len <<= 8; + len += buf[(*offset)++]; + } + } + + return len; +} + +/** + * Skip the ASN1.1 object type and its length. Get ready to read the object's + * data. + */ +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) +{ + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + return get_asn1_length(buf, offset); +} + +/** + * Skip over an ASN.1 object type completely. Get ready to read the next + * object. + */ +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) +{ + int len; + + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + len = get_asn1_length(buf, offset); + *offset += len; + return 0; +} + +/** + * Read an integer value for ASN.1 data + * Note: This function allocates memory which must be freed by the user. + */ +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) +{ + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) + goto end_int_array; + + if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */ + { + len--; + (*offset)++; + } + + *object = (uint8_t *)malloc(len); + memcpy(*object, &buf[*offset], len); + *offset += len; + +end_int_array: + return len; +} + +/** + * Get all the RSA private key specifics from an ASN.1 encoded file + */ +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) +{ + int offset = 7; + uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL; + int mod_len, priv_len, pub_len; +#ifdef CONFIG_BIGINT_CRT + uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL; + int p_len, q_len, dP_len, dQ_len, qInv_len; +#endif + + /* not in der format */ + if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */ + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: This is not a valid ASN.1 file\n"); +#endif + return X509_INVALID_PRIV_KEY; + } + + /* initialise the RNG */ + RNG_initialize(buf, len); + + mod_len = asn1_get_int(buf, &offset, &modulus); + pub_len = asn1_get_int(buf, &offset, &pub_exp); + priv_len = asn1_get_int(buf, &offset, &priv_exp); + + if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) + return X509_INVALID_PRIV_KEY; + +#ifdef CONFIG_BIGINT_CRT + p_len = asn1_get_int(buf, &offset, &p); + q_len = asn1_get_int(buf, &offset, &q); + dP_len = asn1_get_int(buf, &offset, &dP); + dQ_len = asn1_get_int(buf, &offset, &dQ); + qInv_len = asn1_get_int(buf, &offset, &qInv); + + if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) + return X509_INVALID_PRIV_KEY; + + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len, + p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len); + + free(p); + free(q); + free(dP); + free(dQ); + free(qInv); +#else + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len); +#endif + + free(modulus); + free(priv_exp); + free(pub_exp); + return X509_OK; +} + +/** + * Get the time of a certificate. Ignore hours/minutes/seconds. + */ +static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) +{ + int ret = X509_NOT_OK, len, t_offset; + struct tm tm; + + if (buf[(*offset)++] != ASN1_UTC_TIME) + goto end_utc_time; + len = get_asn1_length(buf, offset); + t_offset = *offset; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); + + if (tm.tm_year <= 50) /* 1951-2050 thing */ + { + tm.tm_year += 100; + } + + tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; + tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); + *t = mktime(&tm); + *offset += len; + ret = X509_OK; + +end_utc_time: + return ret; +} + +/** + * Get the version type of a certificate (which we don't actually care about) + */ +int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + (*offset) += 2; /* get past explicit tag */ + if (asn1_skip_obj(cert, offset, ASN1_INTEGER)) + goto end_version; + + ret = X509_OK; +end_version: + return ret; +} + +/** + * Retrieve the notbefore and notafter certificate times. + */ +int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_get_utc_time(cert, offset, &x509_ctx->not_before) || + asn1_get_utc_time(cert, offset, &x509_ctx->not_after)); +} + +/** + * Get the components of a distinguished name + */ +static int asn1_get_oid_x520(const uint8_t *buf, int *offset) +{ + int dn_type = 0; + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto end_oid; + + /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name + components we are interested in. */ + if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04) + dn_type = buf[(*offset)++]; + else + { + *offset += len; /* skip over it */ + } + +end_oid: + return dn_type; +} + +/** + * Obtain an ASN.1 printable string type. + */ +static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) +{ + int len = X509_NOT_OK; + + /* some certs have this awful crud in them for some reason */ + if (buf[*offset] != ASN1_PRINTABLE_STR && + buf[*offset] != ASN1_TELETEX_STR && + buf[*offset] != ASN1_IA5_STR && + buf[*offset] != ASN1_UNICODE_STR) + goto end_pnt_str; + + (*offset)++; + len = get_asn1_length(buf, offset); + + if (buf[*offset - 1] == ASN1_UNICODE_STR) + { + int i; + *str = (char *)malloc(len/2+1); /* allow for null */ + + for (i = 0; i < len; i += 2) + (*str)[i/2] = buf[*offset + i + 1]; + + (*str)[len/2] = 0; /* null terminate */ + } + else + { + *str = (char *)malloc(len+1); /* allow for null */ + memcpy(*str, &buf[*offset], len); + (*str)[len] = 0; /* null terminate */ + } + + *offset += len; + +end_pnt_str: + return len; +} + +/** + * Get the subject name (or the issuer) of a certificate. + */ +int asn1_name(const uint8_t *cert, int *offset, char *dn[]) +{ + int ret = X509_NOT_OK; + int dn_type; + char *tmp = NULL; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_name; + + while (asn1_next_obj(cert, offset, ASN1_SET) >= 0) + { + int i, found = 0; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + (dn_type = asn1_get_oid_x520(cert, offset)) < 0) + goto end_name; + + if (asn1_get_printable_str(cert, offset, &tmp) < 0) + { + free(tmp); + goto end_name; + } + + /* find the distinguished named type */ + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (dn_type == g_dn_types[i]) + { + if (dn[i] == NULL) + { + dn[i] = tmp; + found = 1; + break; + } + } + } + + if (found == 0) /* not found so get rid of it */ + { + free(tmp); + } + } + + ret = X509_OK; +end_name: + return ret; +} + +/** + * Read the modulus and public exponent of a certificate. + */ +int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, mod_len, pub_len; + uint8_t *modulus = NULL, *pub_exp = NULL; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, offset, ASN1_SEQUENCE) || + asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0) + goto end_pub_key; + + (*offset)++; /* ignore the padding bit field */ + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_pub_key; + + mod_len = asn1_get_int(cert, offset, &modulus); + pub_len = asn1_get_int(cert, offset, &pub_exp); + + RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); + + free(modulus); + free(pub_exp); + ret = X509_OK; + +end_pub_key: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Read the signature of the certificate. + */ +int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + if (cert[(*offset)++] != ASN1_BIT_STRING) + goto end_sig; + + x509_ctx->sig_len = get_asn1_length(cert, offset)-1; + (*offset)++; /* ignore bit string padding bits */ + x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len); + memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); + *offset += x509_ctx->sig_len; + ret = X509_OK; + +end_sig: + return ret; +} + +/* + * Compare 2 distinguished name components for equality + * @return 0 if a match + */ +static int asn1_compare_dn_comp(const char *dn1, const char *dn2) +{ + int ret = 1; + + if ((dn1 && dn2 == NULL) || (dn1 == NULL && dn2)) goto err_no_match; + + ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 0; + +err_no_match: + return ret; +} + +/** + * Clean up all of the CA certificates. + */ +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) +{ + int i = 0; + + if (ca_cert_ctx == NULL) + return; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + x509_free(ca_cert_ctx->cert[i]); + ca_cert_ctx->cert[i++] = NULL; + } + + free(ca_cert_ctx); +} + +/* + * Compare 2 distinguished names for equality + * @return 0 if a match + */ +int asn1_compare_dn(char * const dn1[], char * const dn2[]) +{ + int i; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (asn1_compare_dn_comp(dn1[i], dn2[i])) + return 1; + } + + return 0; /* all good */ +} + +#endif + +/** + * Read the signature type of the certificate. We only support RSA-MD5 and + * RSA-SHA1 signature types. + */ +int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, len; + + if (cert[(*offset)++] != ASN1_OID) + goto end_check_sig; + + len = get_asn1_length(cert, offset); + + if (len == 5 && memcmp(sig_iis6_oid, &cert[*offset], + SIG_IIS6_OID_SIZE) == 0) + { + x509_ctx->sig_type = SIG_TYPE_SHA1; + } + else + { + if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) + goto end_check_sig; /* unrecognised cert type */ + + x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; + } + + *offset += len; + asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ + ret = X509_OK; + +end_check_sig: + return ret; +} + diff --git a/libs/nixio/axTLS/ssl/cert.h b/libs/nixio/axTLS/ssl/cert.h new file mode 100644 index 000000000..7a85d2d84 --- /dev/null +++ b/libs/nixio/axTLS/ssl/cert.h @@ -0,0 +1,43 @@ +unsigned char default_certificate[] = { + 0x30, 0x82, 0x01, 0xd7, 0x30, 0x82, 0x01, 0x40, 0x02, 0x09, 0x00, 0xf1, + 0xc3, 0x87, 0xc0, 0xd4, 0xf4, 0x57, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x34, + 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x29, 0x61, + 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x44, 0x6f, 0x64, 0x67, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x30, 0x36, + 0x30, 0x37, 0x31, 0x31, 0x34, 0x34, 0x33, 0x32, 0x5a, 0x17, 0x0d, 0x33, + 0x33, 0x31, 0x30, 0x32, 0x33, 0x31, 0x31, 0x34, 0x34, 0x33, 0x32, 0x5a, + 0x30, 0x2c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x61, 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, + 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, + 0x81, 0x81, 0x00, 0xd8, 0xe0, 0xbf, 0x15, 0xde, 0xea, 0xaf, 0xe8, 0xd5, + 0xfd, 0x0b, 0xa8, 0xa8, 0xb3, 0xd7, 0x46, 0x5d, 0xa7, 0x26, 0x6c, 0x0c, + 0xb5, 0xd9, 0xbc, 0xc6, 0xf8, 0xc0, 0x78, 0xd0, 0xf6, 0x56, 0x65, 0xf8, + 0x29, 0x48, 0x0e, 0x7b, 0x0b, 0xa6, 0x25, 0x7e, 0xe8, 0x7b, 0x79, 0x6f, + 0x38, 0xe5, 0xb5, 0xb7, 0xf4, 0xe0, 0x9c, 0x91, 0x60, 0xf4, 0x06, 0xf3, + 0x40, 0x1e, 0xf9, 0x91, 0x19, 0xa9, 0x2f, 0x47, 0x43, 0xb5, 0x9b, 0x1e, + 0xdc, 0xf6, 0xaa, 0x1c, 0x49, 0x79, 0x21, 0x28, 0xcb, 0xaa, 0x49, 0x73, + 0xd9, 0x09, 0x05, 0x4c, 0x02, 0xf2, 0x4c, 0x4d, 0x6c, 0x1c, 0x80, 0xa7, + 0x14, 0x91, 0x44, 0xfc, 0x12, 0xb3, 0xe1, 0xe7, 0xe3, 0x4f, 0x44, 0xba, + 0x8c, 0xc3, 0x74, 0x39, 0xe8, 0x4c, 0xd0, 0xd4, 0x4c, 0x24, 0x61, 0xb4, + 0x40, 0x95, 0x8c, 0xc0, 0x0a, 0xb7, 0x02, 0x39, 0x31, 0x85, 0x93, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x0b, + 0x47, 0x24, 0x52, 0x7d, 0xb6, 0x63, 0x78, 0xbc, 0x80, 0xdd, 0x87, 0x6c, + 0x90, 0x4c, 0x33, 0xc3, 0x5c, 0xa7, 0x97, 0x09, 0x1c, 0x09, 0x4f, 0x9b, + 0x6e, 0xb3, 0x5a, 0x3e, 0x46, 0x92, 0x1a, 0xc7, 0x87, 0x15, 0x59, 0xe1, + 0x88, 0x5c, 0xce, 0x6a, 0xe2, 0x96, 0xaa, 0x32, 0xec, 0xc2, 0xed, 0x78, + 0x8b, 0xe0, 0x90, 0x66, 0x93, 0x14, 0xc3, 0x98, 0xab, 0x33, 0x35, 0xd3, + 0x7d, 0x5d, 0x51, 0x0a, 0x9c, 0xb9, 0x10, 0x58, 0x47, 0x7a, 0x98, 0x95, + 0x64, 0xff, 0x4c, 0x5d, 0x82, 0x19, 0xf9, 0xea, 0x0f, 0x5e, 0x9a, 0xcb, + 0x32, 0x27, 0x64, 0xca, 0x6f, 0x58, 0x8a, 0xd0, 0xc0, 0x36, 0xf4, 0xb9, + 0x63, 0x34, 0xa5, 0xda, 0x36, 0x50, 0x36, 0x49, 0xd2, 0xb7, 0x3a, 0x21, + 0x33, 0x5b, 0x3e, 0xd6, 0x5f, 0x0c, 0x99, 0x83, 0xb7, 0xb2, 0xf7, 0x8b, + 0x44, 0xc4, 0x5e, 0x73, 0x41, 0xa9, 0x02 +}; +unsigned int default_certificate_len = 475; diff --git a/libs/nixio/axTLS/ssl/crypto_misc.h b/libs/nixio/axTLS/ssl/crypto_misc.h new file mode 100644 index 000000000..97cb0f2d2 --- /dev/null +++ b/libs/nixio/axTLS/ssl/crypto_misc.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. +*/ + +/** + * @file crypto_misc.h + */ + +#ifndef HEADER_CRYPTO_MISC_H +#define HEADER_CRYPTO_MISC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "crypto.h" +#include "bigint.h" + +/************************************************************************** + * X509 declarations + **************************************************************************/ +#define X509_OK 0 +#define X509_NOT_OK -1 +#define X509_VFY_ERROR_NO_TRUSTED_CERT -2 +#define X509_VFY_ERROR_BAD_SIGNATURE -3 +#define X509_VFY_ERROR_NOT_YET_VALID -4 +#define X509_VFY_ERROR_EXPIRED -5 +#define X509_VFY_ERROR_SELF_SIGNED -6 +#define X509_VFY_ERROR_INVALID_CHAIN -7 +#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8 +#define X509_INVALID_PRIV_KEY -9 + +/* + * The Distinguished Name + */ +#define X509_NUM_DN_TYPES 3 +#define X509_COMMON_NAME 0 +#define X509_ORGANIZATION 1 +#define X509_ORGANIZATIONAL_UNIT 2 + +struct _x509_ctx +{ + char *ca_cert_dn[X509_NUM_DN_TYPES]; + char *cert_dn[X509_NUM_DN_TYPES]; + time_t not_before; + time_t not_after; + uint8_t *signature; + uint16_t sig_len; + uint8_t sig_type; + RSA_CTX *rsa_ctx; + bigint *digest; + struct _x509_ctx *next; +}; + +typedef struct _x509_ctx X509_CTX; + +#ifdef CONFIG_SSL_CERT_VERIFICATION +typedef struct +{ + X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS]; +} CA_CERT_CTX; +#endif + +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx); +void x509_free(X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +#endif +#ifdef CONFIG_SSL_FULL_MODE +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx); +const char * x509_display_error(int error); +#endif + +/************************************************************************** + * ASN1 declarations + **************************************************************************/ +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_PRINTABLE_STR 0x13 +#define ASN1_TELETEX_STR 0x14 +#define ASN1_IA5_STR 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_UNICODE_STR 0x1e +#define ASN1_SEQUENCE 0x30 +#define ASN1_SET 0x31 +#define ASN1_IMPLICIT_TAG 0x80 +#define ASN1_EXPLICIT_TAG 0xa0 + +#define SIG_TYPE_MD2 0x02 +#define SIG_TYPE_MD5 0x04 +#define SIG_TYPE_SHA1 0x05 + +int get_asn1_length(const uint8_t *buf, int *offset); +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx); +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object); +int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_name(const uint8_t *cert, int *offset, char *dn[]); +int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_compare_dn(char * const dn1[], char * const dn2[]); +#endif +int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx); + +/************************************************************************** + * MISC declarations + **************************************************************************/ +#define SALT_SIZE 8 + +extern const char * const unsupported_str; + +typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int); +typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +int get_file(const char *filename, uint8_t **buf); + +#if defined(CONFIG_SSL_FULL_MODE) || defined(WIN32) || defined(CONFIG_DEBUG) +EXP_FUNC void STDCALL print_blob(const char *format, const uint8_t *data, int size, ...); +#else + #define print_blob(...) +#endif + +EXP_FUNC int STDCALL base64_decode(const char *in, int len, + uint8_t *out, int *outlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/nixio/axTLS/ssl/gen_cert.c b/libs/nixio/axTLS/ssl/gen_cert.c new file mode 100644 index 000000000..94b74903c --- /dev/null +++ b/libs/nixio/axTLS/ssl/gen_cert.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include "config.h" + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +#include +#include +#include "ssl.h" + +/** + * Generate a basic X.509 certificate + */ + +static uint8_t set_gen_length(int len, uint8_t *buf, int *offset) +{ + if (len < 0x80) /* short form */ + { + buf[(*offset)++] = len; + return 1; + } + else /* long form */ + { + int i, length_bytes = 0; + + if (len & 0x00FF0000) + length_bytes = 3; + else if (len & 0x0000FF00) + length_bytes = 2; + else if (len & 0x000000FF) + length_bytes = 1; + + buf[(*offset)++] = 0x80 + length_bytes; + + for (i = length_bytes-1; i >= 0; i--) + { + buf[*offset+i] = len & 0xFF; + len >>= 8; + } + + *offset += length_bytes; + return length_bytes+1; + } +} + +static int pre_adjust_with_size(uint8_t type, + int *seq_offset, uint8_t *buf, int *offset) +{ + buf[(*offset)++] = type; + *seq_offset = *offset; + *offset += 4; /* fill in later */ + return *offset; +} + +static void adjust_with_size(int seq_size, int seq_start, + uint8_t *buf, int *offset) +{ + uint8_t seq_byte_size; + int orig_seq_size = seq_size; + int orig_seq_start = seq_start; + + seq_size = *offset-seq_size; + seq_byte_size = set_gen_length(seq_size, buf, &seq_start); + + if (seq_byte_size != 4) + { + memmove(&buf[orig_seq_start+seq_byte_size], + &buf[orig_seq_size], seq_size); + *offset -= 4-seq_byte_size; + } +} + +static void gen_serial_number(uint8_t *buf, int *offset) +{ + static const uint8_t ser_oid[] = { ASN1_INTEGER, 1, 0x7F }; + memcpy(&buf[*offset], ser_oid , sizeof(ser_oid)); + *offset += sizeof(ser_oid); +} + +static void gen_signature_alg(uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */ + static const uint8_t sig_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + ASN1_NULL, 0x00 + }; + + memcpy(&buf[*offset], sig_oid, sizeof(sig_oid)); + *offset += sizeof(sig_oid); +} + +static int gen_dn(const char *name, uint8_t dn_type, + uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int name_size = strlen(name); + + if (name_size > 0x70) /* just too big */ + { + ret = X509_NOT_OK; + goto error; + } + + buf[(*offset)++] = ASN1_SET; + set_gen_length(9+name_size, buf, offset); + buf[(*offset)++] = ASN1_SEQUENCE; + set_gen_length(7+name_size, buf, offset); + buf[(*offset)++] = ASN1_OID; + buf[(*offset)++] = 3; + buf[(*offset)++] = 0x55; + buf[(*offset)++] = 0x04; + buf[(*offset)++] = dn_type; + buf[(*offset)++] = ASN1_PRINTABLE_STR; + buf[(*offset)++] = name_size; + strcpy(&buf[*offset], name); + *offset += name_size; + +error: + return ret; +} + +static int gen_issuer(const char * dn[], uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + char fqdn[128]; + + /* we need the common name, so if not configured, work out the fully + * qualified domain name */ + if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0) + { + int fqdn_len; + gethostname(fqdn, sizeof(fqdn)); + fqdn_len = strlen(fqdn); + fqdn[fqdn_len++] = '.'; + getdomainname(&fqdn[fqdn_len], sizeof(fqdn)-fqdn_len); + fqdn_len = strlen(fqdn); + + if (fqdn[fqdn_len-1] == '.') /* ensure '.' is not last char */ + fqdn[fqdn_len-1] = 0; + + dn[X509_COMMON_NAME] = fqdn; + } + + if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset))) + goto error; + + if (dn[X509_ORGANIZATION] != NULL && strlen(dn[X509_ORGANIZATION]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset))) + goto error; + } + + if (dn[X509_ORGANIZATIONAL_UNIT] != NULL && + strlen(dn[X509_ORGANIZATIONAL_UNIT]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_UNIT], 11, buf, offset))) + goto error; + } + + adjust_with_size(seq_size, seq_offset, buf, offset); + +error: + return ret; +} + +static void gen_utc_time(uint8_t *buf, int *offset) +{ + static const uint8_t time_seq[] = + { + ASN1_SEQUENCE, 30, + ASN1_UTC_TIME, 13, + '0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z', + ASN1_UTC_TIME, 13, /* make it good for 30 or so years */ + '3', '8', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' + }; + + /* fixed time */ + memcpy(&buf[*offset], time_seq, sizeof(time_seq)); + *offset += sizeof(time_seq); +} + +static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + static const uint8_t pub_key_seq[] = + { + ASN1_INTEGER, 0x03, 0x01, 0x00, 0x01 /* INTEGER 65537 */ + }; + + int seq_offset; + int pub_key_size = rsa_ctx->num_octets; + uint8_t *block = (uint8_t *)alloca(pub_key_size); + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + buf[(*offset)++] = ASN1_INTEGER; + bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size); + + if (*block & 0x80) /* make integer positive */ + { + set_gen_length(pub_key_size+1, buf, offset); + buf[(*offset)++] = 0; + } + else + set_gen_length(pub_key_size, buf, offset); + + memcpy(&buf[*offset], block, pub_key_size); + *offset += pub_key_size; + memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq)); + *offset += sizeof(pub_key_seq); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void gen_pub_key1(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_BIT_STRING, &seq_offset, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + gen_pub_key2(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) */ + static const uint8_t rsa_enc_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + ASN1_NULL, 0x00 + }; + + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + memcpy(&buf[*offset], rsa_enc_oid, sizeof(rsa_enc_oid)); + *offset += sizeof(rsa_enc_oid); + gen_pub_key1(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst, + uint8_t *buf, int *offset) +{ + static const uint8_t asn1_sig[] = + { + ASN1_SEQUENCE, 0x21, ASN1_SEQUENCE, 0x09, ASN1_OID, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* sha1 (1 3 14 3 2 26) */ + ASN1_NULL, 0x00, ASN1_OCTET_STRING, 0x14 + }; + + uint8_t *enc_block = (uint8_t *)alloca(rsa_ctx->num_octets); + uint8_t *block = (uint8_t *)alloca(sizeof(asn1_sig) + SHA1_SIZE); + int sig_size; + + /* add the digest as an embedded asn.1 sequence */ + memcpy(block, asn1_sig, sizeof(asn1_sig)); + memcpy(&block[sizeof(asn1_sig)], sha_dgst, SHA1_SIZE); + + sig_size = RSA_encrypt(rsa_ctx, block, + sizeof(asn1_sig) + SHA1_SIZE, enc_block, 1); + + buf[(*offset)++] = ASN1_BIT_STRING; + set_gen_length(sig_size+1, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + memcpy(&buf[*offset], enc_block, sig_size); + *offset += sig_size; +} + +static int gen_tbs_cert(const char * dn[], + const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset, + uint8_t *sha_dgst) +{ + int ret = X509_OK; + SHA1_CTX sha_ctx; + int seq_offset; + int begin_tbs = *offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + gen_serial_number(buf, offset); + gen_signature_alg(buf, offset); + + /* CA certicate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_utc_time(buf, offset); + + /* certificate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_pub_key(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + +error: + return ret; +} + +/** + * Create a new certificate. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data) +{ + int ret = X509_OK, offset = 0, seq_offset; + /* allocate enough space to load a new certificate */ + uint8_t *buf = (uint8_t *)alloca(ssl_ctx->rsa_ctx->num_octets*2 + 512); + uint8_t sha_dgst[SHA1_SIZE]; + int seq_size = pre_adjust_with_size(ASN1_SEQUENCE, + &seq_offset, buf, &offset); + + if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0) + goto error; + + gen_signature_alg(buf, &offset); + gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset); + adjust_with_size(seq_size, seq_offset, buf, &offset); + *cert_data = (uint8_t *)malloc(offset); /* create the exact memory for it */ + memcpy(*cert_data, buf, offset); + +error: + return ret < 0 ? ret : offset; +} + +#endif + diff --git a/libs/nixio/axTLS/ssl/loader.c b/libs/nixio/axTLS/ssl/loader.c new file mode 100644 index 000000000..4232f7eec --- /dev/null +++ b/libs/nixio/axTLS/ssl/loader.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Load certificates/keys into memory. These can be in many different formats. + * PEM support and other formats can be processed here. + * + * The PEM private keys may be optionally encrypted with AES128 or AES256. + * The encrypted PEM keys were generated with something like: + * + * openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512 + */ + +#include +#include +#include + +#include "ssl.h" + +static int do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#ifdef CONFIG_SSL_HAS_PEM +static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#endif + +/* + * Load a file into memory that is in binary DER (or ascii PEM) format. + */ +EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, + const char *filename, const char *password) +{ +#ifndef CONFIG_SSL_SKELETON_MODE + static const char * const begin = "-----BEGIN"; + int ret = SSL_OK; + SSLObjLoader *ssl_obj = NULL; + + if (filename == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + ssl_obj->len = get_file(filename, &ssl_obj->buf); + if (ssl_obj->len <= 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + /* is the file a PEM file? */ + if (strncmp((char *)ssl_obj->buf, begin, strlen(begin)) == 0) + { +#ifdef CONFIG_SSL_HAS_PEM + ret = ssl_obj_PEM_load(ssl_ctx, obj_type, ssl_obj, password); +#else + printf(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + } + else + ret = do_obj(ssl_ctx, obj_type, ssl_obj, password); + +error: + ssl_obj_free(ssl_obj); + return ret; +#else + printf(unsupported_str); + return SSL_ERROR_NOT_SUPPORTED; +#endif /* CONFIG_SSL_SKELETON_MODE */ +} + +/* + * Transfer binary data into the object loader. + */ +EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type, + const uint8_t *data, int len, const char *password) +{ + int ret; + SSLObjLoader *ssl_obj; + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + ssl_obj->buf = (uint8_t *)malloc(len); + memcpy(ssl_obj->buf, data, len); + ssl_obj->len = len; + ret = do_obj(ssl_ctx, mem_type, ssl_obj, password); + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Actually work out what we are doing + */ +static int do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + int ret = SSL_OK; + + switch (obj_type) + { + case SSL_OBJ_RSA_KEY: + ret = add_private_key(ssl_ctx, ssl_obj); + break; + + case SSL_OBJ_X509_CERT: + ret = add_cert(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_OBJ_X509_CACERT: + ret = add_cert_auth(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; +#endif + +#ifdef CONFIG_SSL_USE_PKCS12 + case SSL_OBJ_PKCS8: + ret = pkcs8_decode(ssl_ctx, ssl_obj, password); + break; + + case SSL_OBJ_PKCS12: + ret = pkcs12_decode(ssl_ctx, ssl_obj, password); + break; +#endif + default: + printf(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; + break; + } + + return ret; +} + +/* + * Clean up our mess. + */ +void ssl_obj_free(SSLObjLoader *ssl_obj) +{ + if (ssl_obj) + { + free(ssl_obj->buf); + free(ssl_obj); + } +} + +/* + * Support for PEM encoded keys/certificates. + */ +#ifdef CONFIG_SSL_HAS_PEM + +#define NUM_PEM_TYPES 3 +#define IV_SIZE 16 +#define IS_RSA_PRIVATE_KEY 0 +#define IS_ENCRYPTED_PRIVATE_KEY 1 +#define IS_CERTIFICATE 2 + +static const char * const begins[NUM_PEM_TYPES] = +{ + "-----BEGIN RSA PRIVATE KEY-----", + "-----BEGIN ENCRYPTED PRIVATE KEY-----", + "-----BEGIN CERTIFICATE-----", +}; + +static const char * const ends[NUM_PEM_TYPES] = +{ + "-----END RSA PRIVATE KEY-----", + "-----END ENCRYPTED PRIVATE KEY-----", + "-----END CERTIFICATE-----", +}; + +static const char * const aes_str[2] = +{ + "DEK-Info: AES-128-CBC,", + "DEK-Info: AES-256-CBC," +}; + +/** + * Take a base64 blob of data and decrypt it (using AES) into its + * proper ASN.1 form. + */ +static int pem_decrypt(const char *where, const char *end, + const char *password, SSLObjLoader *ssl_obj) +{ + int ret = -1; + int is_aes_256 = 0; + char *start = NULL; + uint8_t iv[IV_SIZE]; + int i, pem_size; + MD5_CTX md5_ctx; + AES_CTX aes_ctx; + uint8_t key[32]; /* AES256 size */ + + if (password == NULL || strlen(password) == 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Need a password for this PEM file\n"); TTY_FLUSH(); +#endif + goto error; + } + + if ((start = strstr((const char *)where, aes_str[0]))) /* AES128? */ + { + start += strlen(aes_str[0]); + } + else if ((start = strstr((const char *)where, aes_str[1]))) /* AES256? */ + { + is_aes_256 = 1; + start += strlen(aes_str[1]); + } + else + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Unsupported password cipher\n"); TTY_FLUSH(); +#endif + goto error; + } + + /* convert from hex to binary - assumes uppercase hex */ + for (i = 0; i < IV_SIZE; i++) + { + char c = *start++ - '0'; + iv[i] = (c > 9 ? c + '0' - 'A' + 10 : c) << 4; + c = *start++ - '0'; + iv[i] += (c > 9 ? c + '0' - 'A' + 10 : c); + } + + while (*start == '\r' || *start == '\n') + start++; + + /* turn base64 into binary */ + pem_size = (int)(end-start); + if (base64_decode(start, pem_size, ssl_obj->buf, &ssl_obj->len) != 0) + goto error; + + /* work out the key */ + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(key, &md5_ctx); + + if (is_aes_256) + { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, key, MD5_SIZE); + MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(&key[MD5_SIZE], &md5_ctx); + } + + /* decrypt using the key/iv */ + AES_set_key(&aes_ctx, key, iv, is_aes_256 ? AES_MODE_256 : AES_MODE_128); + AES_convert_key(&aes_ctx); + AES_cbc_decrypt(&aes_ctx, ssl_obj->buf, ssl_obj->buf, ssl_obj->len); + ret = 0; + +error: + return ret; +} + +/** + * Take a base64 blob of data and turn it into its proper ASN.1 form. + */ +static int new_pem_obj(SSL_CTX *ssl_ctx, int is_cacert, char *where, + int remain, const char *password) +{ + int ret = SSL_OK; + SSLObjLoader *ssl_obj = NULL; + int i, pem_size, obj_type; + char *start = NULL, *end = NULL; + + for (i = 0; i < NUM_PEM_TYPES; i++) + { + if ((start = strstr(where, begins[i])) && + (end = strstr(where, ends[i]))) + { + remain -= (int)(end-start); + start += strlen(begins[i]); + pem_size = (int)(end-start); + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + + /* 4/3 bigger than what we need but so what */ + ssl_obj->buf = (uint8_t *)calloc(1, pem_size); + + if (i == IS_RSA_PRIVATE_KEY && + strstr(start, "Proc-Type:") && + strstr(start, "4,ENCRYPTED")) + { + /* check for encrypted PEM file */ + if (pem_decrypt(start, end, password, ssl_obj) < 0) + goto error; + } + else if (base64_decode(start, pem_size, + ssl_obj->buf, &ssl_obj->len) != 0) + goto error; + + switch (i) + { + case IS_RSA_PRIVATE_KEY: + obj_type = SSL_OBJ_RSA_KEY; + break; + + case IS_ENCRYPTED_PRIVATE_KEY: + obj_type = SSL_OBJ_PKCS8; + break; + + case IS_CERTIFICATE: + obj_type = is_cacert ? + SSL_OBJ_X509_CACERT : SSL_OBJ_X509_CERT; + break; + + default: + goto error; + } + + /* In a format we can now understand - so process it */ + if ((ret = do_obj(ssl_ctx, obj_type, ssl_obj, password))) + goto error; + + end += strlen(ends[i]); + remain -= strlen(ends[i]); + while (remain > 0 && (*end == '\r' || *end == '\n')) + { + end++; + remain--; + } + + break; + } + } + + if (i == NUM_PEM_TYPES) + goto error; + + /* more PEM stuff to process? */ + if (remain) + ret = new_pem_obj(ssl_ctx, is_cacert, end, remain, password); + +error: + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Load a file into memory that is in ASCII PEM format. + */ +static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + char *start; + + /* add a null terminator */ + ssl_obj->len++; + ssl_obj->buf = (uint8_t *)realloc(ssl_obj->buf, ssl_obj->len); + ssl_obj->buf[ssl_obj->len-1] = 0; + start = (char *)ssl_obj->buf; + return new_pem_obj(ssl_ctx, obj_type == SSL_OBJ_X509_CACERT, + start, ssl_obj->len, password); +} +#endif /* CONFIG_SSL_HAS_PEM */ + +/** + * Load the key/certificates in memory depending on compile-time and user + * options. + */ +int load_key_certs(SSL_CTX *ssl_ctx) +{ + int ret = SSL_OK; + uint32_t options = ssl_ctx->options; +#ifdef CONFIG_SSL_GENERATE_X509_CERT + uint8_t *cert_data = NULL; + int cert_size; + static const char *dn[] = + { + CONFIG_SSL_X509_COMMON_NAME, + CONFIG_SSL_X509_ORGANIZATION_NAME, + CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + }; +#endif + + /* do the private key first */ + if (strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, + CONFIG_SSL_PRIVATE_KEY_LOCATION, + CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few more bytes */ +#include "private_key.h" + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key, + default_private_key_len, NULL); +#endif + } + + /* now load the certificate */ +#ifdef CONFIG_SSL_GENERATE_X509_CERT + if ((cert_size = ssl_x509_create(ssl_ctx, 0, dn, &cert_data)) < 0) + { + ret = cert_size; + goto error; + } + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL); + free(cert_data); +#else + if (strlen(CONFIG_SSL_X509_CERT_LOCATION)) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, + CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few bytes and RAM */ +#include "cert.h" + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, + default_certificate, default_certificate_len, NULL); +#endif + } +#endif + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + printf("Error: Certificate or key not loaded\n"); TTY_FLUSH(); + } +#endif + + return ret; + +} diff --git a/libs/nixio/axTLS/ssl/openssl.c b/libs/nixio/axTLS/ssl/openssl.c new file mode 100644 index 000000000..b6b955008 --- /dev/null +++ b/libs/nixio/axTLS/ssl/openssl.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/* + * Enable a subset of openssl compatible functions. We don't aim to be 100% + * compatible - just to be able to do basic ports etc. + * + * Only really tested on mini_httpd, so I'm not too sure how extensive this + * port is. + */ + +#include "config.h" + +#ifdef CONFIG_OPENSSL_COMPATIBLE +#include +#include +#include +#include "ssl.h" + +#define OPENSSL_CTX_ATTR ((OPENSSL_CTX *)ssl_ctx->bonus_attr) + +static char *key_password = NULL; + +void *SSLv23_server_method(void) { return NULL; } +void *SSLv3_server_method(void) { return NULL; } +void *TLSv1_server_method(void) { return NULL; } +void *SSLv23_client_method(void) { return NULL; } +void *SSLv3_client_method(void) { return NULL; } +void *TLSv1_client_method(void) { return NULL; } + +typedef void * (*ssl_func_type_t)(void); +typedef void * (*bio_func_type_t)(void); + +typedef struct +{ + ssl_func_type_t ssl_func_type; +} OPENSSL_CTX; + +SSL_CTX * SSL_CTX_new(ssl_func_type_t meth) +{ + SSL_CTX *ssl_ctx = ssl_ctx_new(0, 5); + ssl_ctx->bonus_attr = malloc(sizeof(OPENSSL_CTX)); + OPENSSL_CTX_ATTR->ssl_func_type = meth; + return ssl_ctx; +} + +void SSL_CTX_free(SSL_CTX * ssl_ctx) +{ + free(ssl_ctx->bonus_attr); + ssl_ctx_free(ssl_ctx); +} + +SSL * SSL_new(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + ssl_func_type_t ssl_func_type; + + ssl = ssl_new(ssl_ctx, -1); /* fd is set later */ + ssl_func_type = OPENSSL_CTX_ATTR->ssl_func_type; + +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (ssl_func_type == SSLv23_client_method || + ssl_func_type == SSLv3_client_method || + ssl_func_type == TLSv1_client_method) + { + SET_SSL_FLAG(SSL_IS_CLIENT); + } + else +#endif + { + ssl->next_state = HS_CLIENT_HELLO; + } + + return ssl; +} + +int SSL_set_fd(SSL *s, int fd) +{ + s->client_fd = fd; + return 1; /* always succeeds */ +} + +int SSL_accept(SSL *ssl) +{ + while (ssl_read(ssl, NULL) == SSL_OK) + { + if (ssl->next_state == HS_CLIENT_HELLO) + return 1; /* we're done */ + } + + return -1; +} + +#ifdef CONFIG_SSL_ENABLE_CLIENT +int SSL_connect(SSL *ssl) +{ + return do_client_connect(ssl) == SSL_OK ? 1 : -1; +} +#endif + +void SSL_free(SSL *ssl) +{ + ssl_free(ssl); +} + +int SSL_read(SSL *ssl, void *buf, int num) +{ + uint8_t *read_buf; + int ret; + + while ((ret = ssl_read(ssl, &read_buf)) == SSL_OK); + + if (ret > SSL_OK) + { + memcpy(buf, read_buf, ret > num ? num : ret); + } + + return ret; +} + +int SSL_write(SSL *ssl, const void *buf, int num) +{ + return ssl_write(ssl, buf, num); +} + +int SSL_CTX_use_certificate_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, file, key_password) == SSL_OK); +} + +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ssl_ctx, int len, const uint8_t *d) +{ + return (ssl_obj_memory_load(ssl_ctx, + SSL_OBJ_X509_CERT, d, len, NULL) == SSL_OK); +} + +int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, + unsigned int sid_ctx_len) +{ + return 1; +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) +{ + return 1; +} + +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ssl_ctx, const char *file) +{ + return (ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int SSL_shutdown(SSL *ssl) +{ + return 1; +} + +/*** get/set session ***/ +SSL_SESSION *SSL_get1_session(SSL *ssl) +{ + return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */ +} + +int SSL_set_session(SSL *ssl, SSL_SESSION *session) +{ + memcpy(ssl->session_id, (uint8_t *)session, SSL_SESSION_ID_SIZE); + return 1; +} + +void SSL_SESSION_free(SSL_SESSION *session) { } +/*** end get/set session ***/ + +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) +{ + return 0; +} + +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, + int (*verify_callback)(int, void *)) { } + +void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth) { } + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath) +{ + return 1; +} + +void *SSL_load_client_CA_file(const char *file) +{ + return (void *)file; +} + +void SSL_CTX_set_client_CA_list(SSL_CTX *ssl_ctx, void *file) +{ + + ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, (const char *)file, NULL); +} + +void SSLv23_method(void) { } + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, void *cb) { } + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u) +{ + key_password = (char *)u; +} + +int SSL_peek(SSL *ssl, void *buf, int num) +{ + memcpy(buf, ssl->bm_data, num); + return num; +} + +void SSL_set_bio(SSL *ssl, void *rbio, void *wbio) { } + +long SSL_get_verify_result(const SSL *ssl) +{ + return ssl_handshake_status(ssl); +} + +int SSL_state(SSL *ssl) +{ + return 0x03; // ok state +} + +/** end of could do better list */ + +void *SSL_get_peer_certificate(const SSL *ssl) +{ + return &ssl->ssl_ctx->certs[0]; +} + +int SSL_clear(SSL *ssl) +{ + return 1; +} + + +int SSL_CTX_check_private_key(const SSL_CTX *ctx) +{ + return 1; +} + +int SSL_CTX_set_cipher_list(SSL *s, const char *str) +{ + return 1; +} + +int SSL_get_error(const SSL *ssl, int ret) +{ + ssl_display_error(ret); + return 0; /* TODO: return proper return code */ +} + +void SSL_CTX_set_options(SSL_CTX *ssl_ctx, int option) {} +int SSL_library_init(void ) { return 1; } +void SSL_load_error_strings(void ) {} +void ERR_print_errors_fp(FILE *fp) {} + +#ifndef CONFIG_SSL_SKELETON_MODE +long SSL_CTX_get_timeout(const SSL_CTX *ssl_ctx) { + return CONFIG_SSL_EXPIRY_TIME*3600; } +long SSL_CTX_set_timeout(SSL_CTX *ssl_ctx, long t) { + return SSL_CTX_get_timeout(ssl_ctx); } +#endif +void BIO_printf(FILE *f, const char *format, ...) +{ + va_list(ap); + va_start(ap, format); + vfprintf(f, format, ap); + va_end(ap); +} + +void* BIO_s_null(void) { return NULL; } +FILE *BIO_new(bio_func_type_t func) +{ + if (func == BIO_s_null) + return fopen("/dev/null", "r"); + else + return NULL; +} + +FILE *BIO_new_fp(FILE *stream, int close_flag) { return stream; } +int BIO_free(FILE *a) { if (a != stdout && a != stderr) fclose(a); return 1; } + + + +#endif diff --git a/libs/nixio/axTLS/ssl/os_port.c b/libs/nixio/axTLS/ssl/os_port.c new file mode 100644 index 000000000..6a71000b4 --- /dev/null +++ b/libs/nixio/axTLS/ssl/os_port.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @file os_port.c + * + * OS specific functions. + */ +#include +#include +#include +#include +#include "os_port.h" + +#ifdef WIN32 +/** + * gettimeofday() not in Win32 + */ +EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone) +{ +#if defined(_WIN32_WCE) + t->tv_sec = time(NULL); + t->tv_usec = 0; /* 1sec precision only */ +#else + struct _timeb timebuffer; + _ftime(&timebuffer); + t->tv_sec = (long)timebuffer.time; + t->tv_usec = 1000 * timebuffer.millitm; /* 1ms precision */ +#endif +} + +/** + * strcasecmp() not in Win32 + */ +EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2) +{ + while (tolower(*s1) == tolower(*s2++)) + { + if (*s1++ == '\0') + { + return 0; + } + } + + return *(unsigned char *)s1 - *(unsigned char *)(s2 - 1); +} + + +EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size) +{ + HKEY hKey; + unsigned long datatype; + unsigned long bufferlength = buf_size; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), + 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + return -1; + + RegQueryValueEx(hKey, "Domain", NULL, &datatype, buf, &bufferlength); + RegCloseKey(hKey); + return 0; +} +#endif + +#undef malloc +#undef realloc +#undef calloc + +static const char * out_of_mem_str = "out of memory"; +static const char * file_open_str = "Could not open file \"%s\""; + +/* + * Some functions that call display some error trace and then call abort(). + * This just makes life much easier on embedded systems, since we're + * suffering major trauma... + */ +EXP_FUNC void * STDCALL ax_malloc(size_t s) +{ + void *x; + + if ((x = malloc(s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s) +{ + void *x; + + if ((x = realloc(y, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s) +{ + void *x; + + if ((x = calloc(n, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC int STDCALL ax_open(const char *pathname, int flags) +{ + int x; + + if ((x = open(pathname, flags)) < 0) + exit_now(file_open_str, pathname); + + return x; +} + +/** + * This is a call which will deliberately exit an application, but will + * display some information before dying. + */ +void exit_now(const char *format, ...) +{ + va_list argp; + + va_start(argp, format); + vfprintf(stderr, format, argp); + va_end(argp); + abort(); +} + diff --git a/libs/nixio/axTLS/ssl/os_port.h b/libs/nixio/axTLS/ssl/os_port.h new file mode 100644 index 000000000..262c4dd70 --- /dev/null +++ b/libs/nixio/axTLS/ssl/os_port.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @file os_port.h + * + * Some stuff to minimise the differences between windows and linux/unix + */ + +#ifndef HEADER_OS_PORT_H +#define HEADER_OS_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(WIN32) +#define STDCALL __stdcall +#define EXP_FUNC __declspec(dllexport) +#else +#define STDCALL +#define EXP_FUNC +#endif + +#if defined(_WIN32_WCE) +#undef WIN32 +#define WIN32 +#endif + +#ifdef WIN32 + +/* Windows CE stuff */ +#if defined(_WIN32_WCE) +#include +#define abort() exit(1) +#else +#include +#include +#include +#include +#endif /* _WIN32_WCE */ + +#include +#include +#undef getpid +#undef open +#undef close +#undef sleep +#undef gettimeofday +#undef dup2 +#undef unlink + +#define SOCKET_READ(A,B,C) recv(A,B,C,0) +#define SOCKET_WRITE(A,B,C) send(A,B,C,0) +#define SOCKET_CLOSE(A) closesocket(A) +#define SOCKET_BLOCK(A) u_long argp = 0; \ + ioctlsocket(A, FIONBIO, &argp) +#define srandom(A) srand(A) +#define random() rand() +#define getpid() _getpid() +#define snprintf _snprintf +#define open(A,B) _open(A,B) +#define dup2(A,B) _dup2(A,B) +#define unlink(A) _unlink(A) +#define close(A) _close(A) +#define read(A,B,C) _read(A,B,C) +#define write(A,B,C) _write(A,B,C) +#define sleep(A) Sleep(A*1000) +#define usleep(A) Sleep(A/1000) +#define strdup(A) _strdup(A) +#define chroot(A) _chdir(A) +#define chdir(A) _chdir(A) +#define alloca(A) _alloca(A) +#ifndef lseek +#define lseek(A,B,C) _lseek(A,B,C) +#endif + +/* This fix gets around a problem where a win32 application on a cygwin xterm + doesn't display regular output (until a certain buffer limit) - but it works + fine under a normal DOS window. This is a hack to get around the issue - + see http://www.khngai.com/emacs/tty.php */ +#define TTY_FLUSH() if (!_isatty(_fileno(stdout))) fflush(stdout); + +/* + * automatically build some library dependencies. + */ +#pragma comment(lib, "WS2_32.lib") +#pragma comment(lib, "AdvAPI32.lib") + +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +typedef UINT64 uint64_t; +typedef INT64 int64_t; +typedef int socklen_t; + +EXP_FUNC void STDCALL gettimeofday(struct timeval* t,void* timezone); +EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2); +EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size); + +#else /* Not Win32 */ + +#ifdef CONFIG_PLATFORM_SOLARIS +#include +#else +#include +#endif /* Not Solaris */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET_READ(A,B,C) read(A,B,C) +#define SOCKET_WRITE(A,B,C) write(A,B,C) +#define SOCKET_CLOSE(A) close(A) +#define SOCKET_BLOCK(A) int fd = fcntl(A, F_GETFL, NULL); \ + fcntl(A, F_SETFL, fd & ~O_NONBLOCK) +#define TTY_FLUSH() + +#endif /* Not Win32 */ + +/* some functions to mutate the way these work */ +#define malloc(A) ax_malloc(A) +#ifndef realloc +#define realloc(A,B) ax_realloc(A,B) +#endif +#define calloc(A,B) ax_calloc(A,B) + +EXP_FUNC void * STDCALL ax_malloc(size_t s); +EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s); +EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s); +EXP_FUNC int STDCALL ax_open(const char *pathname, int flags); + +#ifdef CONFIG_PLATFORM_LINUX +void exit_now(const char *format, ...) __attribute((noreturn)); +#else +void exit_now(const char *format, ...); +#endif + +/* Mutexing definitions */ +#if defined(CONFIG_SSL_CTX_MUTEXING) +#if defined(WIN32) +#define SSL_CTX_MUTEX_TYPE HANDLE +#define SSL_CTX_MUTEX_INIT(A) A=CreateMutex(0, FALSE, 0) +#define SSL_CTX_MUTEX_DESTROY(A) CloseHandle(A) +#define SSL_CTX_LOCK(A) WaitForSingleObject(A, INFINITE) +#define SSL_CTX_UNLOCK(A) ReleaseMutex(A) +#else +#include +#define SSL_CTX_MUTEX_TYPE pthread_mutex_t +#define SSL_CTX_MUTEX_INIT(A) pthread_mutex_init(&A, NULL) +#define SSL_CTX_MUTEX_DESTROY(A) pthread_mutex_destroy(&A) +#define SSL_CTX_LOCK(A) pthread_mutex_lock(&A) +#define SSL_CTX_UNLOCK(A) pthread_mutex_unlock(&A) +#endif +#else /* no mutexing */ +#define SSL_CTX_MUTEX_INIT(A) +#define SSL_CTX_MUTEX_DESTROY(A) +#define SSL_CTX_LOCK(A) +#define SSL_CTX_UNLOCK(A) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/nixio/axTLS/ssl/p12.c b/libs/nixio/axTLS/ssl/p12.c new file mode 100644 index 000000000..6ed92e431 --- /dev/null +++ b/libs/nixio/axTLS/ssl/p12.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Process PKCS#8/PKCS#12 keys. + * + * The decoding of a PKCS#12 key is fairly specific - this code was tested on a + * key generated with: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 + * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd + * + * or with a certificate chain: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe + * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd + * + * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The + * private/public keys/certs have to use RSA encryption. Both the integrity + * and privacy passwords are the same. + * + * The PKCS#8 files were generated with something like: + * + * PEM format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 + * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 + * + * DER format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER + * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 + */ + +#include +#include +#include + +#include "ssl.h" + +/* all commented out if not used */ +#ifdef CONFIG_SSL_USE_PKCS12 + +#define BLOCK_SIZE 64 +#define PKCS12_KEY_ID 1 +#define PKCS12_IV_ID 2 +#define PKCS12_MAC_ID 3 + +static char *make_uni_pass(const char *password, int *uni_pass_len); +static int p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id); +static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key); +static int get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations); + +/* + * Take a raw pkcs8 block and then decrypt it and turn it into a normal key. + */ +int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int len, offset = 0; + int iterations; + int ret = SSL_NOT_OK; + uint8_t *version = NULL; + const uint8_t *salt; + uint8_t *priv_key; + int uni_pass_len; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Invalid p8 ASN.1 file\n"); +#endif + goto error; + } + + /* unencrypted key? */ + if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0) + { + ret = p8_add_key(ssl_ctx, buf); + goto error; + } + + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0) + goto error; + + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + priv_key = &buf[offset]; + + p8_decrypt(uni_pass, uni_pass_len, salt, + iterations, priv_key, len, PKCS12_KEY_ID); + ret = p8_add_key(ssl_ctx, priv_key); + +error: + free(version); + free(uni_pass); + return ret; +} + +/* + * Take the unencrypted pkcs8 and turn it into a private key + */ +static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key) +{ + uint8_t *buf = priv_key; + int len, offset = 0; + int ret = SSL_NOT_OK; + + /* Skip the preamble and go straight to the private key. + We only support rsaEncryption (1.2.840.113549.1.1.1) */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx); + +error: + return ret; +} + +/* + * Create the unicode password + */ +static char *make_uni_pass(const char *password, int *uni_pass_len) +{ + int pass_len = 0, i; + char *uni_pass; + + if (password == NULL) + { + password = ""; + } + + uni_pass = (char *)malloc((strlen(password)+1)*2); + + /* modify the password into a unicode version */ + for (i = 0; i < (int)strlen(password); i++) + { + uni_pass[pass_len++] = 0; + uni_pass[pass_len++] = password[i]; + } + + uni_pass[pass_len++] = 0; /* null terminate */ + uni_pass[pass_len++] = 0; + *uni_pass_len = pass_len; + return uni_pass; +} + +/* + * Decrypt a pkcs8 block. + */ +static int p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id) +{ + uint8_t p[BLOCK_SIZE*2]; + uint8_t d[BLOCK_SIZE]; + uint8_t Ai[SHA1_SIZE]; + SHA1_CTX sha_ctx; + RC4_CTX rc4_ctx; + int i; + + for (i = 0; i < BLOCK_SIZE; i++) + { + p[i] = salt[i % SALT_SIZE]; + p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len]; + d[i] = id; + } + + /* get the key - no IV since we are using RC4 */ + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, d, sizeof(d)); + SHA1_Update(&sha_ctx, p, sizeof(p)); + SHA1_Final(Ai, &sha_ctx); + + for (i = 1; i < iter; i++) + { + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, Ai, SHA1_SIZE); + SHA1_Final(Ai, &sha_ctx); + } + + /* do the decryption */ + if (id == PKCS12_KEY_ID) + { + RC4_setup(&rc4_ctx, Ai, 16); + RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len); + } + else /* MAC */ + memcpy(priv_key, Ai, SHA1_SIZE); + + return 0; +} + +/* + * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s) + * and keys. + */ +int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int all_ok = 0, len, iterations, auth_safes_start, + auth_safes_end, auth_safes_len, key_offset, offset = 0; + int all_certs = 0; + uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac; + uint8_t key[SHA1_SIZE]; + uint8_t mac[SHA1_SIZE]; + const uint8_t *salt; + int uni_pass_len, ret; + int error_code = SSL_ERROR_NOT_SUPPORTED; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + static const uint8_t pkcs_data[] = /* pkc7 data */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 }; + static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 }; + static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 }; + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Invalid p12 ASN.1 file\n"); +#endif + goto error; + } + + if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3) + { + error_code = SSL_ERROR_INVALID_VERSION; + goto error; + } + + /* remove all the boring pcks7 bits */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0) + goto error; + + /* work out the MAC start/end points (done on AuthSafes) */ + auth_safes_start = offset; + auth_safes_end = offset; + if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0) + goto error; + + auth_safes_len = auth_safes_end - auth_safes_start; + auth_safes = malloc(auth_safes_len); + + memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs_encrypted) || + memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted)))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + /* work out the salt for the certificate */ + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0) + goto error; + + /* decrypt the certificate */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the certificate */ + key_offset = 0; + all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE); + + /* keep going until all certs are loaded */ + while (key_offset < all_certs) + { + int cert_offset = key_offset; + + if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0) + goto error; + + if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0) + goto error; + + key_offset = cert_offset; + } + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs8_key_bag)) || + memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag))) + goto error; + + offset += len; + + /* work out the salt for the private key */ + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + /* decrypt the private key */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the private key */ + if ((ret = p8_add_key(ssl_ctx, cert)) < 0) + goto error; + + /* miss out on friendly name, local key id etc */ + if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0) + goto error; + + /* work out the MAC */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || + len != SHA1_SIZE) + goto error; + + orig_mac = &buf[offset]; + offset += len; + + /* get the salt */ + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8) + goto error; + + salt = &buf[offset]; + + /* work out what the mac should be */ + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, + key, SHA1_SIZE, PKCS12_MAC_ID)) < 0) + goto error; + + hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac); + + if (memcmp(mac, orig_mac, SHA1_SIZE)) + { + error_code = SSL_ERROR_INVALID_HMAC; + goto error; + } + + all_ok = 1; + +error: + free(version); + free(uni_pass); + free(auth_safes); + return all_ok ? SSL_OK : error_code; +} + +/* + * Retrieve the salt/iteration details from a PBE block. + */ +static int get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations) +{ + static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 }; + + int i, len; + uint8_t *iter = NULL; + int error_code = SSL_ERROR_NOT_SUPPORTED; + + /* Get the PBE type */ + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto error; + + /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1) + which is the only algorithm we support */ + if (len != sizeof(pbeSH1RC4) || + memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4))) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n"); +#endif + goto error; + } + + *offset += len; + + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 || + len != 8) + goto error; + + *salt = &buf[*offset]; + *offset += len; + + if ((len = asn1_get_int(buf, offset, &iter)) < 0) + goto error; + + *iterations = 0; + for (i = 0; i < len; i++) + { + (*iterations) <<= 8; + (*iterations) += iter[i]; + } + + free(iter); + error_code = SSL_OK; /* got here - we are ok */ + +error: + return error_code; +} + +#endif diff --git a/libs/nixio/axTLS/ssl/private_key.h b/libs/nixio/axTLS/ssl/private_key.h new file mode 100644 index 000000000..96a525331 --- /dev/null +++ b/libs/nixio/axTLS/ssl/private_key.h @@ -0,0 +1,54 @@ +unsigned char default_private_key[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xd8, + 0xe0, 0xbf, 0x15, 0xde, 0xea, 0xaf, 0xe8, 0xd5, 0xfd, 0x0b, 0xa8, 0xa8, + 0xb3, 0xd7, 0x46, 0x5d, 0xa7, 0x26, 0x6c, 0x0c, 0xb5, 0xd9, 0xbc, 0xc6, + 0xf8, 0xc0, 0x78, 0xd0, 0xf6, 0x56, 0x65, 0xf8, 0x29, 0x48, 0x0e, 0x7b, + 0x0b, 0xa6, 0x25, 0x7e, 0xe8, 0x7b, 0x79, 0x6f, 0x38, 0xe5, 0xb5, 0xb7, + 0xf4, 0xe0, 0x9c, 0x91, 0x60, 0xf4, 0x06, 0xf3, 0x40, 0x1e, 0xf9, 0x91, + 0x19, 0xa9, 0x2f, 0x47, 0x43, 0xb5, 0x9b, 0x1e, 0xdc, 0xf6, 0xaa, 0x1c, + 0x49, 0x79, 0x21, 0x28, 0xcb, 0xaa, 0x49, 0x73, 0xd9, 0x09, 0x05, 0x4c, + 0x02, 0xf2, 0x4c, 0x4d, 0x6c, 0x1c, 0x80, 0xa7, 0x14, 0x91, 0x44, 0xfc, + 0x12, 0xb3, 0xe1, 0xe7, 0xe3, 0x4f, 0x44, 0xba, 0x8c, 0xc3, 0x74, 0x39, + 0xe8, 0x4c, 0xd0, 0xd4, 0x4c, 0x24, 0x61, 0xb4, 0x40, 0x95, 0x8c, 0xc0, + 0x0a, 0xb7, 0x02, 0x39, 0x31, 0x85, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0x94, 0x07, 0x72, 0xe5, 0xbe, 0xad, 0x79, 0x3b, + 0xf7, 0x33, 0x2c, 0x8e, 0x05, 0xf8, 0x1a, 0x6b, 0xd0, 0xe8, 0x91, 0xf5, + 0x16, 0x07, 0xd9, 0x82, 0x5c, 0x5c, 0xd5, 0x22, 0xa1, 0x9e, 0x42, 0x02, + 0x7f, 0x8b, 0xcd, 0xbe, 0xf4, 0x85, 0x52, 0xf6, 0x2c, 0xd5, 0x09, 0xd2, + 0x2c, 0xf4, 0x2c, 0xf6, 0x07, 0x85, 0x80, 0xf9, 0xdc, 0xd0, 0xcc, 0x3f, + 0x22, 0x31, 0x15, 0xf3, 0x49, 0xf2, 0xb5, 0xe2, 0x69, 0x99, 0x04, 0x04, + 0x49, 0x21, 0xdb, 0x9f, 0xa1, 0x54, 0x5a, 0xfa, 0xe4, 0xd9, 0xf9, 0x07, + 0x05, 0xff, 0x9a, 0x65, 0xa4, 0xeb, 0xf2, 0x47, 0xce, 0x56, 0xc7, 0x72, + 0x49, 0x48, 0x5c, 0xe8, 0x14, 0xd7, 0x8f, 0x25, 0xcc, 0x49, 0x29, 0x06, + 0x6a, 0x54, 0x7b, 0x17, 0xdc, 0x9e, 0xd4, 0x53, 0xf0, 0xf5, 0x9e, 0x85, + 0x25, 0xa1, 0xeb, 0x3d, 0xe9, 0x2f, 0xb9, 0x9c, 0xf6, 0xe1, 0x80, 0x81, + 0x02, 0x41, 0x00, 0xee, 0x02, 0x78, 0xc7, 0x78, 0x85, 0x04, 0x97, 0xcc, + 0x36, 0xbd, 0xd6, 0x11, 0xe2, 0xc7, 0x39, 0xd9, 0x34, 0x51, 0x72, 0x6f, + 0x8a, 0x0f, 0xcd, 0x88, 0x32, 0x33, 0x9b, 0xc7, 0xa7, 0x03, 0x77, 0xd9, + 0x82, 0x35, 0xb6, 0xdd, 0x1f, 0xc2, 0xc1, 0x13, 0x40, 0x83, 0x55, 0xeb, + 0x60, 0xeb, 0x81, 0x8e, 0x0c, 0x16, 0x62, 0xb4, 0xb4, 0x3c, 0xeb, 0x08, + 0x80, 0x9c, 0x79, 0xd3, 0x38, 0xca, 0xf1, 0x02, 0x41, 0x00, 0xe9, 0x45, + 0x5f, 0x2e, 0x16, 0xcc, 0x93, 0x50, 0x40, 0xb6, 0x79, 0xbc, 0x38, 0xe0, + 0x56, 0x68, 0x50, 0xd3, 0x2f, 0x73, 0x8c, 0x8c, 0x2a, 0x0e, 0x81, 0x4a, + 0x8a, 0xbb, 0xcc, 0xf0, 0x64, 0x34, 0x46, 0x9f, 0x07, 0x7d, 0x22, 0xb6, + 0xf9, 0x46, 0xac, 0x57, 0x23, 0x8c, 0x1e, 0xeb, 0xd3, 0x05, 0x4d, 0xa8, + 0x83, 0x6a, 0x67, 0xf6, 0xa6, 0xb1, 0xab, 0x8e, 0xc1, 0xef, 0xef, 0x7d, + 0xf0, 0xc3, 0x02, 0x40, 0x2f, 0xc6, 0x59, 0x3e, 0x18, 0xe8, 0x02, 0x73, + 0x01, 0xef, 0xdf, 0x0d, 0x30, 0x4b, 0xe8, 0x17, 0xa9, 0x8c, 0xc1, 0xe8, + 0x89, 0x91, 0x19, 0xf8, 0xf4, 0xa4, 0xb7, 0x0d, 0x46, 0xf7, 0x34, 0x50, + 0x03, 0x5e, 0x0a, 0xb0, 0x29, 0x14, 0xae, 0x00, 0x19, 0x80, 0x32, 0x9c, + 0xb5, 0x81, 0x9f, 0xe4, 0x42, 0x82, 0x14, 0xa0, 0x3d, 0x8b, 0x8c, 0x4a, + 0xd5, 0x4b, 0x13, 0x9d, 0xb4, 0x93, 0x4a, 0xd1, 0x02, 0x40, 0x64, 0x8c, + 0x83, 0x77, 0x61, 0x5a, 0x73, 0x11, 0x3f, 0xa3, 0xa8, 0x1b, 0x8a, 0xc4, + 0xa0, 0x5a, 0x3c, 0xa4, 0x9b, 0x2a, 0x8a, 0x65, 0x8c, 0x67, 0x4e, 0x31, + 0xac, 0x55, 0x41, 0x04, 0x49, 0x9d, 0x02, 0xe7, 0xdf, 0x99, 0x7f, 0xd2, + 0x30, 0xe6, 0xd6, 0xb8, 0x84, 0xd9, 0x0c, 0x27, 0x08, 0x81, 0x9b, 0xb4, + 0xcc, 0x58, 0x9c, 0x51, 0x84, 0x0e, 0xc7, 0x6d, 0x34, 0x89, 0x50, 0xc9, + 0x0f, 0x73, 0x02, 0x41, 0x00, 0xda, 0xde, 0x5e, 0x1a, 0xac, 0x1d, 0x1d, + 0xd7, 0xb9, 0x65, 0x26, 0x00, 0xf5, 0xd4, 0xe4, 0x28, 0x84, 0x86, 0x2f, + 0x00, 0x9c, 0x41, 0x00, 0x52, 0xe1, 0x47, 0x91, 0xc0, 0x52, 0x05, 0x4e, + 0x0f, 0x2f, 0x0d, 0xca, 0x9b, 0x3d, 0x89, 0x41, 0xbf, 0xee, 0x9f, 0xa1, + 0xe6, 0x9d, 0xa4, 0xeb, 0x45, 0x7f, 0xe3, 0xcb, 0xa4, 0x6b, 0x0a, 0xe2, + 0x7e, 0xb0, 0x87, 0x5c, 0x40, 0xb1, 0x51, 0x11, 0x1d +}; +unsigned int default_private_key_len = 609; diff --git a/libs/nixio/axTLS/ssl/ssl.h b/libs/nixio/axTLS/ssl/ssl.h new file mode 100644 index 000000000..539d0a305 --- /dev/null +++ b/libs/nixio/axTLS/ssl/ssl.h @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @mainpage axTLS API + * + * @image html axolotl.jpg + * + * The axTLS library has features such as: + * - The TLSv1 SSL client/server protocol + * - No requirement to use any openssl libraries. + * - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers. + * - RSA encryption/decryption with variable sized keys (up to 4096 bits). + * - Certificate chaining and peer authentication. + * - Session resumption, session renegotiation. + * - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding. + * - Highly configurable compile time options. + * - Portable across many platforms (written in ANSI C), and has language + * bindings in C, C#, VB.NET, Java, Perl and Lua. + * - Partial openssl API compatibility (via a wrapper). + * - A very small footprint (around 50-60kB for the library in 'server-only' + * mode). + * - No dependencies on sockets - can use serial connections for example. + * - A very simple API - ~ 20 functions/methods. + * + * A list of these functions/methods are described below. + * + * @ref c_api + * + * @ref bigint_api + * + * @ref csharp_api + * + * @ref java_api + */ +#ifndef HEADER_SSL_H +#define HEADER_SSL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +//#include "crypto.h" + +/* need to predefine before ssl_lib.h gets to it */ +#define SSL_SESSION_ID_SIZE 32 + +#include "tls1.h" + +/* The optional parameters that can be given to the client/server SSL engine */ +#define SSL_CLIENT_AUTHENTICATION 0x00010000 +#define SSL_SERVER_VERIFY_LATER 0x00020000 +#define SSL_NO_DEFAULT_KEY 0x00040000 +#define SSL_DISPLAY_STATES 0x00080000 +#define SSL_DISPLAY_BYTES 0x00100000 +#define SSL_DISPLAY_CERTS 0x00200000 +#define SSL_DISPLAY_RSA 0x00400000 + +/* errors that can be generated */ +#define SSL_OK 0 +#define SSL_NOT_OK -1 +#define SSL_ERROR_DEAD -2 +#define SSL_ERROR_CONN_LOST -256 +#define SSL_ERROR_SOCK_SETUP_FAILURE -258 +#define SSL_ERROR_INVALID_HANDSHAKE -260 +#define SSL_ERROR_INVALID_PROT_MSG -261 +#define SSL_ERROR_INVALID_HMAC -262 +#define SSL_ERROR_INVALID_VERSION -263 +#define SSL_ERROR_INVALID_SESSION -265 +#define SSL_ERROR_NO_CIPHER -266 +#define SSL_ERROR_BAD_CERTIFICATE -268 +#define SSL_ERROR_INVALID_KEY -269 +#define SSL_ERROR_FINISHED_INVALID -271 +#define SSL_ERROR_NO_CERT_DEFINED -272 +#define SSL_ERROR_NOT_SUPPORTED -274 +#define SSL_X509_OFFSET -512 +#define SSL_X509_ERROR(A) (SSL_X509_OFFSET+A) + +/* these are all the alerts that are recognized */ +#define SSL_ALERT_CLOSE_NOTIFY 0 +#define SSL_ALERT_UNEXPECTED_MESSAGE 10 +#define SSL_ALERT_BAD_RECORD_MAC 20 +#define SSL_ALERT_HANDSHAKE_FAILURE 40 +#define SSL_ALERT_BAD_CERTIFICATE 42 +#define SSL_ALERT_ILLEGAL_PARAMETER 47 +#define SSL_ALERT_DECODE_ERROR 50 +#define SSL_ALERT_DECRYPT_ERROR 51 +#define SSL_ALERT_INVALID_VERSION 70 + +/* The ciphers that are supported */ +#define SSL_AES128_SHA 0x2f +#define SSL_AES256_SHA 0x35 +#define SSL_RC4_128_SHA 0x05 +#define SSL_RC4_128_MD5 0x04 + +/* build mode ids' */ +#define SSL_BUILD_SKELETON_MODE 0x01 +#define SSL_BUILD_SERVER_ONLY 0x02 +#define SSL_BUILD_ENABLE_VERIFICATION 0x03 +#define SSL_BUILD_ENABLE_CLIENT 0x04 +#define SSL_BUILD_FULL_MODE 0x05 + +/* offsets to retrieve configuration information */ +#define SSL_BUILD_MODE 0 +#define SSL_MAX_CERT_CFG_OFFSET 1 +#define SSL_MAX_CA_CERT_CFG_OFFSET 2 +#define SSL_HAS_PEM 3 + +/* default session sizes */ +#define SSL_DEFAULT_SVR_SESS 5 +#define SSL_DEFAULT_CLNT_SESS 1 + +/* X.509/X.520 distinguished name types */ +#define SSL_X509_CERT_COMMON_NAME 0 +#define SSL_X509_CERT_ORGANIZATION 1 +#define SSL_X509_CERT_ORGANIZATIONAL_NAME 2 +#define SSL_X509_CA_CERT_COMMON_NAME 3 +#define SSL_X509_CA_CERT_ORGANIZATION 4 +#define SSL_X509_CA_CERT_ORGANIZATIONAL_NAME 5 + +/* SSL object loader types */ +#define SSL_OBJ_X509_CERT 1 +#define SSL_OBJ_X509_CACERT 2 +#define SSL_OBJ_RSA_KEY 3 +#define SSL_OBJ_PKCS8 4 +#define SSL_OBJ_PKCS12 5 + +/** + * @defgroup c_api Standard C API + * @brief The standard interface in C. + * @{ + */ + +/** + * @brief Establish a new client/server context. + * + * This function is called before any client/server SSL connections are made. + * + * Each new connection will use the this context's private key and + * certificate chain. If a different certificate chain is required, then a + * different context needs to be be used. + * + * There are two threading models supported - a single thread with one + * SSL_CTX can support any number of SSL connections - and multiple threads can + * support one SSL_CTX object each (the default). But if a single SSL_CTX + * object uses many SSL objects in individual threads, then the + * CONFIG_SSL_CTX_MUTEXING option needs to be configured. + * + * @param options [in] Any particular options. At present the options + * supported are: + * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server + * authentication fails. The certificate can be authenticated later with a + * call to ssl_verify_cert(). + * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication + * i.e. each handshake will include a "certificate request" message from the + * server. Only available if verification has been enabled. + * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences + * during the handshake. + * - SSL_DISPLAY_STATES (full mode build only): Display the state changes + * during the handshake. + * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that + * are passed during a handshake. + * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that + * are passed during a handshake. + * + * @param num_sessions [in] The number of sessions to be used for session + * caching. If this value is 0, then there is no session caching. This option + * is not used in skeleton mode. + * @return A client/server context. + */ +EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions); + +/** + * @brief Remove a client/server context. + * + * Frees any used resources used by this context. Each connection will be + * sent a "Close Notify" alert (if possible). + * @param ssl_ctx [in] The client/server context. + */ +EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx); + +/** + * @brief (server only) Establish a new SSL connection to an SSL client. + * + * It is up to the application to establish the logical connection (whether it + * is a socket, serial connection etc). + * @param ssl_ctx [in] The server context. + * @param client_fd [in] The client's file descriptor. + * @return An SSL object reference. + */ +EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd); + +/** + * @brief (client only) Establish a new SSL connection to an SSL server. + * + * It is up to the application to establish the initial logical connection + * (whether it is a socket, serial connection etc). + * + * This is a blocking call - it will finish when the handshake is complete (or + * has failed). + * @param ssl_ctx [in] The client context. + * @param client_fd [in] The client's file descriptor. + * @param session_id [in] A 32 byte session id for session resumption. This + * can be null if no session resumption is being used or required. This option + * is not used in skeleton mode. + * @param sess_id_size The size of the session id (max 32) + * @return An SSL object reference. Use ssl_handshake_status() to check + * if a handshake succeeded. + */ +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size); + +/** + * @brief Free any used resources on this connection. + + * A "Close Notify" message is sent on this connection (if possible). It is up + * to the application to close the socket or file descriptor. + * @param ssl [in] The ssl object reference. + */ +EXP_FUNC void STDCALL ssl_free(SSL *ssl); + +/** + * @brief Read the SSL data stream. + * The socket must be in blocking mode. + * @param ssl [in] An SSL object reference. + * @param in_data [out] If the read was successful, a pointer to the read + * buffer will be here. Do NOT ever free this memory as this buffer is used in + * sucessive calls. If the call was unsuccessful, this value will be null. + * @return The number of decrypted bytes: + * - if > 0, then the handshaking is complete and we are returning the number + * of decrypted bytes. + * - SSL_OK if the handshaking stage is successful (but not yet complete). + * - < 0 if an error. + * @see ssl.h for the error code list. + * @note Use in_data before doing any successive ssl calls. + */ +EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data); + +/** + * @brief Write to the SSL data stream. + * The socket must be in blocking mode. + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @param out_len [in] The number of bytes to be written. + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len); + +/** + * @brief Find an ssl object based on a file descriptor. + * + * Goes through the list of SSL objects maintained in a client/server context + * to look for a file descriptor match. + * @param ssl_ctx [in] The client/server context. + * @param client_fd [in] The file descriptor. + * @return A reference to the SSL object. Returns null if the object could not + * be found. + */ +EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd); + +/** + * @brief Get the session id for a handshake. + * + * This will be a 32 byte sequence and is available after the first + * handshaking messages are sent. + * @param ssl [in] An SSL object reference. + * @return The session id as a 32 byte sequence. + * @note A SSLv23 handshake may have only 16 valid bytes. + */ +EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl); + +/** + * @brief Get the session id size for a handshake. + * + * This will normally be 32 but could be 0 (no session id) or something else. + * @param ssl [in] An SSL object reference. + * @return The size of the session id. + */ +EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl); + +/** + * @brief Return the cipher id (in the SSL form). + * @param ssl [in] An SSL object reference. + * @return The cipher id. This will be one of the following: + * - SSL_AES128_SHA (0x2f) + * - SSL_AES256_SHA (0x35) + * - SSL_RC4_128_SHA (0x05) + * - SSL_RC4_128_MD5 (0x04) + */ +EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl); + +/** + * @brief Return the status of the handshake. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the handshake is complete and ok. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl); + +/** + * @brief Retrieve various parameters about the axTLS engine. + * @param offset [in] The configuration offset. It will be one of the following: + * - SSL_BUILD_MODE The build mode. This will be one of the following: + * - SSL_BUILD_SERVER_ONLY (basic server mode) + * - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication) + * - SSL_BUILD_ENABLE_CLIENT (client/server capabilties) + * - SSL_BUILD_FULL_MODE (client/server with diagnostics) + * - SSL_BUILD_SKELETON_MODE (skeleton mode) + * - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed. + * - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed. + * - SSL_HAS_PEM 1 if supported + * @return The value of the requested parameter. + */ +EXP_FUNC int STDCALL ssl_get_config(int offset); + +/** + * @brief Display why the handshake failed. + * + * This call is only useful in a 'full mode' build. The output is to stdout. + * @param error_code [in] An error code. + * @see ssl.h for the error code list. + */ +EXP_FUNC void STDCALL ssl_display_error(int error_code); + +/** + * @brief Authenticate a received certificate. + * + * This call is usually made by a client after a handshake is complete and the + * context is in SSL_SERVER_VERIFY_LATER mode. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the certificate is verified. + */ +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl); + +/** + * @brief Retrieve an X.509 distinguished name component. + * + * When a handshake is complete and a certificate has been exchanged, then the + * details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's common + * name matches the URL. + * + * A full handshake needs to occur for this call to work properly. + * + * @param ssl [in] An SSL object reference. + * @param component [in] one of: + * - SSL_X509_CERT_COMMON_NAME + * - SSL_X509_CERT_ORGANIZATION + * - SSL_X509_CERT_ORGANIZATIONAL_NAME + * - SSL_X509_CA_CERT_COMMON_NAME + * - SSL_X509_CA_CERT_ORGANIZATION + * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + * @return The appropriate string (or null if not defined) + * @note Verification build mode must be enabled. + */ +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component); + +/** + * @brief Force the client to perform its handshake again. + * + * For a client this involves sending another "client hello" message. + * For the server is means sending a "hello request" message. + * + * This is a blocking call on the client (until the handshake completes). + * + * @param ssl [in] An SSL object reference. + * @return SSL_OK if renegotiation instantiation was ok + */ +EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl); + +/** + * @brief Process a file that is in binary DER or ASCII PEM format. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the file. Can be one of: + * - SSL_OBJ_X509_CERT (no password required) + * - SSL_OBJ_X509_CACERT (no password required) + * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + * - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported) + * - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported) + * + * PEM files are automatically detected (if supported). The object type is + * also detected, and so is not relevant for these types of files. + * @param filename [in] The location of a file in DER/PEM format. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @note Not available in skeleton build mode. + */ +EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password); + +/** + * @brief Process binary data. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the memory data. + * @param data [in] The binary data to be loaded. + * @param len [in] The amount of data to be loaded. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @see ssl_obj_load for more details on obj_type. + */ +EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password); + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +/** + * @brief Create an X.509 certificate. + * + * This certificate is a self-signed v1 cert with a fixed start/stop validity + * times. It is signed with an internal private key in ssl_ctx. + * + * @param ssl_ctx [in] The client/server context. + * @param options [in] Not used yet. + * @param dn [in] An array of distinguished name strings. The array is defined + * by: + * - SSL_X509_CERT_COMMON_NAME (0) + * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the + * hostname will be used. + * - SSL_X509_CERT_ORGANIZATION (1) + * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME + * will be used. + * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) + * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. + * @param cert_data [out] The certificate as a sequence of bytes. + * @return < 0 if an error, or the size of the certificate in bytes. + * @note cert_data must be freed when there is no more need for it. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data); +#endif + +/** + * @brief Return the axTLS library version as a string. + */ +EXP_FUNC const char * STDCALL ssl_version(void); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/nixio/axTLS/ssl/test/Makefile b/libs/nixio/axTLS/ssl/test/Makefile new file mode 100644 index 000000000..56c711f19 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/Makefile @@ -0,0 +1,97 @@ +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +all: + +AXTLS_HOME=../.. + +include $(AXTLS_HOME)/config/.config +include $(AXTLS_HOME)/config/makefile.conf + +ifdef CONFIG_PERFORMANCE_TESTING +all: performance +endif + +ifdef CONFIG_SSL_TEST +all: ssltesting +endif + +include $(AXTLS_HOME)/config/makefile.post + +ifndef CONFIG_PLATFORM_WIN32 +performance: $(AXTLS_HOME)/$(STAGE)/perf_bigint +ssltesting: $(AXTLS_HOME)/$(STAGE)/ssltest +LIBS=$(AXTLS_HOME)/$(STAGE) + +$(AXTLS_HOME)/$(STAGE)/perf_bigint: perf_bigint.o $(LIBS)/libaxtls.a + $(CC) $(LDFLAGS) -o $@ $^ -L $(LIBS) -laxtls + +$(AXTLS_HOME)/$(STAGE)/ssltest: ssltest.o $(LIBS)/libaxtls.a + $(CC) $(LDFLAGS) -o $@ $^ -lpthread -L $(LIBS) -laxtls +else +performance: $(AXTLS_HOME)/$(STAGE)/perf_bigint.exe +ssltesting: $(AXTLS_HOME)/$(STAGE)/ssltest.exe + +CRYPTO_PATH="$(AXTLS_INCLUDE)crypto\\" +AXTLS_SSL_PATH="$(AXTLS_INCLUDE)ssl\\" + +CRYPTO_OBJ=\ + $(CRYPTO_PATH)aes.obj \ + $(CRYPTO_PATH)bigint.obj \ + $(CRYPTO_PATH)crypto_misc.obj \ + $(CRYPTO_PATH)hmac.obj \ + $(CRYPTO_PATH)md2.obj \ + $(CRYPTO_PATH)md5.obj \ + $(CRYPTO_PATH)rc4.obj \ + $(CRYPTO_PATH)rsa.obj \ + $(CRYPTO_PATH)sha1.obj + +OBJ=\ + $(AXTLS_SSL_PATH)asn1.obj \ + $(AXTLS_SSL_PATH)gen_cert.obj \ + $(AXTLS_SSL_PATH)loader.obj \ + $(AXTLS_SSL_PATH)openssl.obj \ + $(AXTLS_SSL_PATH)os_port.obj \ + $(AXTLS_SSL_PATH)p12.obj \ + $(AXTLS_SSL_PATH)x509.obj \ + $(AXTLS_SSL_PATH)tls1.obj \ + $(AXTLS_SSL_PATH)tls1_svr.obj \ + $(AXTLS_SSL_PATH)tls1_clnt.obj + +$(AXTLS_HOME)/$(STAGE)/perf_bigint.exe: perf_bigint.obj + $(LD) $(LDFLAGS) /out:$@ $? $(CRYPTO_OBJ) $(OBJ) + +$(AXTLS_HOME)/$(STAGE)/ssltest.exe: ssltest.obj + $(LD) $(LDFLAGS) /out:$@ $? $(CRYPTO_OBJ) $(OBJ) +endif + +clean:: + -@rm -f $(AXTLS_HOME)/$(STAGE)/perf_bigint* $(AXTLS_HOME)/$(STAGE)/ssltest* + diff --git a/libs/nixio/axTLS/ssl/test/axTLS.ca_key.pem b/libs/nixio/axTLS/ssl/test/axTLS.ca_key.pem new file mode 100644 index 000000000..7c8ac8af2 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.ca_key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCnZdk20fYWh8O6kDTt0AuJWyp0YIrb7W1UNNMPXI5wA4J59IVj +Nmk5wocm9+Hqzbg7rORAN/mHPBhzLAjhnm1HODs36hW15DtbDkkH4wCM/Tsyv79m +n0xq1V6peK3t9vi2D4p/IRjHkYR2jm+BeknopijhY0kHHfpGTHa2DnVirwIDAQAB +AoGAd4Ia5SxYiBU9A0BYyT8yPUm8sYELIaAL4YYk+F6Xwhh/Whnb8MyzquzaGFP4 +Ee30jYYNHlvX5VheDDtvy8OTN5FgKNNdzvW15iA4Hxje04ZI7W87G7OIxm7aYRid +sG4XqZBtsOdj33IRd9hgozywGJ2qRqS6nn2KxRv1w07RniECQQDZAlKxijdn+vQ7 +8/8mXzC+FwQtzeTUCuLrBJcos9I/591ABoxYkWcYLxpFqgCEVwb1qfPBJkL07JPt +Fu6CTnBFAkEAxXmUBs47x5QM99qyBO5UwW0Ksrm/WD4guaaxzQShMt/HzgJl613z +/x4FtxiQJHAr6r2K0t5xTJx89LVKuouYYwJAImue6DAvJ5wDfzrtXo28snn+HLHK +uONdKL/apgcXszE4w74GJsoxWwGlniUf3d3b6b1iP2GtPyIDOJjpjduZLQJAE4jS +VtYB3d1MZxxQLeKxqayyuTlcr0r+C79sqT5C//hZGIzuLhlOMLd0k0cvwxsBjSgQ +2ok8pfp49fAVI1z5xwJAVmJgLc/mSti5A2q3c8HW8qvMJEDPWbpb7p8pg4ePtpa8 +EE3TO4O4J2H+k40C397km4yZXdkNQsiT1zVljJZpiw== +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.ca_x509.cer b/libs/nixio/axTLS/ssl/test/axTLS.ca_x509.cer new file mode 100644 index 0000000000000000000000000000000000000000..9c9936b8e98d0b7475e522377e64ee5cb7d9858c GIT binary patch literal 483 zcmXqLV!UtA#OT4q$uKvs*kYO0(kKI7HcqWJkGAi;jEtmp zDoN&tJf@_eyO<6*KV zyX-GFpR#RyrAh0Vm>C%uk(~z&JZ7M~WVst|nJiE@P*UHyEW+SG%xvc~wa?bgyU%&o z__O9cjynut$9A5`TwES67IMd>m+OQ}^u4t{;lgJg9l!sr3OSV*OvR3NHYY8-f#GI I<)^zO0PSqO5dZ)H literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.ca_x509.pem b/libs/nixio/axTLS/ssl/test/axTLS.ca_x509.pem new file mode 100644 index 000000000..86f659710 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.ca_x509.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB3zCCAUgCCQCdbnM4pjqlWjANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylh +eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNjA2 +MDcxMTQ0MzJaFw0zMzEwMjMxMTQ0MzJaMDQxMjAwBgNVBAoTKWF4VExTIFByb2pl +Y3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQCnZdk20fYWh8O6kDTt0AuJWyp0YIrb7W1UNNMPXI5wA4J59IVj +Nmk5wocm9+Hqzbg7rORAN/mHPBhzLAjhnm1HODs36hW15DtbDkkH4wCM/Tsyv79m +n0xq1V6peK3t9vi2D4p/IRjHkYR2jm+BeknopijhY0kHHfpGTHa2DnVirwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAB0LgNo0oCcwIie5plgwwFybQ8x95q6e3wndM/Mp +3gjcAFbGuchpo3dfFlTcRI0KyERb3q1MVxPM4sff9nT7EdHVyK9s8/ITkP2dcTKc +flbcTEfJVIeM8L2P5F41Hvn9GuGcMW8EmsC06gdbp1LLnqsdrXdMNBsAUBXfgPrU ++UcZ +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.device_key b/libs/nixio/axTLS/ssl/test/axTLS.device_key new file mode 100644 index 0000000000000000000000000000000000000000..4e981d143e1383828b8a76e18c57e0b0f59b202f GIT binary patch literal 609 zcmV-n0-pUaf&yIv0RRGlfdJGZ4h<43zjh$NmAe*gFd;CkIEWYaUz#Bpms2~d;TZ=+ zk`Pe_4BeOCv@NwmC65?N)gXrn?#|TyB9lU`OINiLyb68LKj!YuC$?l`)u2L1ZuNgc z=Sux+ln*4$Nfhl{gttWP`YNkiPpo0p0h3)_$nwg2s|lRS$t9_kyc+@o0RRC4fq?*^ zL=H=j!{3Frp6Ni0WB0Agp#h-CLCM~qOnu)|K)UjF%r+-Ic-(fn!L=2LAE_OnV2@ezYPqfv?7EVw ztnGa>p_(hpRr+n(N$Ooc;-Ug;rmFgzfA1^_2-*k;^0%11vl#+G0M`^95MJ-`(OR-i zBA|9yFh78sA*+l|0wMujX*I%@o4%O>ok-BGDtQP3_l0oh2@5c16ixpA!QhEf#zLZF zQvyK%tS>_pbrT+q^2G&mI(6scS|AxC&V>TLsLms2K>j$f{xgTlW2bz`iWbV^SZp~M zSQa|OOK(li0kpVqV}#k^0zZ3z5inI#a)-o>oxVh6us#Gh)Z2ij5G5&LpFzr;@!ous zU+nPW;~#)v8B_>M%wA;5u?2*padHnE!MbMQTY+(0Wgg|0 v3cKQF;e2bA5N6M*E~R!7Tl|5&J97n<934}R7|5Rx8jwm@UwAr_9k^bwbVnG3 literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.device_key.pem b/libs/nixio/axTLS/ssl/test/axTLS.device_key.pem new file mode 100644 index 000000000..2bcf5e37b --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.device_key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDUIg4NEiu/diDAlbsWbTAhMKw4iBf2X5ohGJdTO6vhGQdEkhBR +Bgzdl9+0LbVDJY8YStUghwnuztT+IpNCrUtXtRK8Cn3QP+buzSe2ZGPVoEJIbvV/ +QudK/WuUDyTNSRTtW4S3RO36KqtbT6xh1QGTXV3I8sp7qwmcysklqZW8GwIDAQAB +AoGBAKBEDkuPw9+Ftp7pQIxj963LoQGgyEHJ3p9Mfd9TQLrydsw2cf9Uy9mKiWcN +9VkCgkZ/Gt/VRgrW1pIduxXv6O+8S14An+2mTayy3Ga1N6MulD7OHQP9kqR4j8TT +xaYPR/1skjhQ+Y0Uw4NEa3OkQp6lAUEp1aVX/mTfIZBguaUxAkEA/H543Ha6wbUV +iB+pHaBgj1nzarmuEey6kqqs7X0zoZory1X6bdpJ6l0/4qICa6aq+pt/7ywJCNoI +CPK3mL2zGQJBANcUHRBe7/HRWrJNIqB2WDA/gJshq4xOAiIBXWk1wpabvpkCnUjQ +rip5CAL3hXDnCQswZxRN/v7B4IlSxkKiY1MCQQCsL0MUdRMejfLFBXI6defjWiAZ +I86FAr6oziNnQP44sf4zh8pjp3zIihbK4lhsORhYFjrES29NzgG0uHBjhNnhAj97 +gBEwVVNyh8SMnb5EZbA+BDjU24CmECUpYZ9Bypzx3nyTX+zw4uMfgGAZVAhLzF5l +DmYiQqcpoipMsDsoCBcCQQCxBYSicXIPG8G6ZuFbgXFcZR7llgq74mbhfGuVEGbP +qS6ldhJb/IG9O3MFlRwdU44YyJ8QGpBKWF94OpIduF6w +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.encrypted.p8 b/libs/nixio/axTLS/ssl/test/axTLS.encrypted.p8 new file mode 100644 index 0000000000000000000000000000000000000000..8b0a7eb4117de421cfb917de7f03055fdfe2c28f GIT binary patch literal 385 zcmV-{0e=24f&qOn90m$1hDe6@4FL=R0Wb~(2zv&jLbrq;i2?!$00e>oTecB0S5Ch{ zHl7H=qA4HIOB;)ah$8h!;8it(u3j>D`liQs0*+k*%XW@PAMoO|ZVtaORrZR~IdUoh zNf*1NZ0jCJOsIuor2OuhY5SBS)gmT$Plt&Ub|ujkSvY}HF3T6ZV$+;=Cj1_ zqiV0Xy#~l}wq>xTcs~vcY@+sZ^yo(jDeDIm8}2%S8s3H&bnWkZz@9kPK8*&28D$i9 z3|2M?_7i99)`(fX3rvXr@m91pnk>4N^QQR5XPP=(qj)V!5qKV+KU4NR01UFqcL=0O z)N0{uLA$ f>0oNt1g(0Zzb?&!uhc`Lu&kDME*dK^i&ZfFoN%%v literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.encrypted_pem.p8 b/libs/nixio/axTLS/ssl/test/axTLS.encrypted_pem.p8 new file mode 100644 index 000000000..19ca3c5ea --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.encrypted_pem.p8 @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBfTAcBgoqhkiG9w0BDAEBMA4ECN+YmhCv0ILdAgIIAASCAVu0QEfMkp0xUsNq +0Ek4Nsa/uxcs8N/2P7Ae7qCakkvsdRvvPPH0y+wuj5NgrG6WpPeeEx9fI2oNNTfC +pwncH0Xm99ofVrgMX6XC45LDZtzXNSZd4TdBP6xvlYXbuGegp5GPJ8emzscHCFhC +JfPHemRAcB7DhiWukPosuSUr5R8OluEMJrQLHuQtlDAvMjLEI98lSchPxF8LKCk3 +SS2uCcmc+4WiR0nHG9BOaGi38+PytHAnbfo1mfVSQzLfgLicMAVGysfQ9QOgpQOO +ygYfM/s7Duwbl0rshyXVJP+7BpYJnPtHvO4BTiizU7ZEr4WBiEnnANDrupSdsxeH ++cxZo70YJVdoPdgMd2ke6EIkUhp7HughFg+okldlEtJA4muKeEzwAxZu0TqxOtZ8 +UYRS4Ygk+rN7Y0qTKSYwSkrFBwUDkpctYjRUOeAZ/mYMKWmMn1ejAb5Is7bjEIxl +tw== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_1024 b/libs/nixio/axTLS/ssl/test/axTLS.key_1024 new file mode 100644 index 0000000000000000000000000000000000000000..5b6ba1d037b002b0d62c9903a3582daac05f77e2 GIT binary patch literal 609 zcmV-n0-pUaf&yIv0RRGlfdJUxzZKro+(m=R>yKlNL=U?*N-L4NhtAFS(rd;edexK>+Roc*l5!1eeSb!19#biHMZR!!od?jgH`Kb>w%687GkutJnIO6oO#nY%JBk00O>_vE*8v_P(ZeM zyg1-iXi(EHbBv5C4uMLFyUg%pG)A8XeImB`MyyvOj2`RL1x=`fYG?MQv8#^3@9%x^ z!va7r##ufX=mK*A@81nDOXwG=jKS!Mks0{(q_+)5_cTxgUJ9@&6s`anfHIu5fuH0; zf)t=Vi;POuOB0>6lS0gAIAV!eeFdnK&`0|5X50)hbn0KrYIAk0gtB;cu1_*7~DLp$s41j1L{dy$8u z>Q{6SrHuFJ5=4!AZ6f&-rB0VNk}EF%GnIau0>!zBGX8BCm(}Gomw{5otF4q+j_!p^p1sm3^l)5OV-`8fG^47KzM3=dOYqWQN}Mx08R?P zYG(QI^V0Ya2D(mOHZ6n3J@*&>6Q$zUudW;ASZ zQzH5(Vi)P&Q`Izs^HP*fY*{R7YiqEtv!@=|O8tlwC^M*Cwj0|J48 z0LvBOMMw2v^dO~mza7xFv4n50Qe?_on9muo``SJVw9twDxz;$tm3C(+A5JQ?3KbZA zjJgHmx*IBX4Ilw`JwQ-@w86>Eta4z4TXD9^xK10mFm*b9*FGd6pGoDEYM0?EA=1$? zAh{7-VIk);QUektIB7B1=h0D7V+h(3j{<>!Ay@B-IXxJRi!d!JY9)Q_y>BYaL$g&; zytn>PR~(ZYzYOl@OGA*<38Cgn$53>6_^#wDST1{Xw-R%AJ5f%kWK&JFV-I!P2AUax z?0^i4+Xou6@fpn6>Md z0)c=X@A}4A#y*Q%XsE;Fq)6I8SgLZZ^7^m458-?+{^(mFM#e>F-0j)c{*7c|js3Dusa$NJOJF%*s<_@CX`P5&9Zxl{ zJ26krm{pSK35pUk>#y4J{_H2XK9P;o*ErV#fq*qDV<*E0RC9D5w-*D^YaVVcH?W@L z7s;4%??z;ZXmnf~khf-nE#R3RkTMy4Ep0;5M@#wpmeJ?wh-{#-F%}3)vYB3 ztJJUoqYbWSsq{2`FhC1&(maox?BB8At*_qHegN_Cm(UerlhGFqSp+Cz&}#6X`{ z2H3(|N2wGExlD97fXsOeCn^W`4Pn++s+M#D>;O(~wfl~y;qgj?b^u`WqW``cC|ig? z96y!M7XS2>*B=ru_+Qy_9la*KLK^5a-5uR~wh=@#U7Tn=6AH}gihl`NTe>q(s6__@ z%!kaMhJ)^ct&_f>W0CD_-U+Y4I0SM3pkvJ2 zC5#@2b&4ibCx^XoK}a?;7P^WLB95^r%-L{Z^TI${%`T)LB-5txgX;UR9*9tfG zp0JoMr}gZ3&ElIoiQP({zW|?|Q29=N}D%WseeY63jXE#yASA&jVGam|+W!jT0{up!{AH7z;XMwF|1&oLlT zIf-10|5X50)heo0Ic&e?J7A6sOJ1oEf|dfVdPFl7nV#Utbk!IcE+x| zwzL2mzN#?8R)=NF$soY8eCHCB3H|LyF9zTm1EmhU@HP^xA!jo7)<0XT9bP4MXZ}Tg z9vpLn130B*c(tPYd~`tvpU+SK#>$+(${mFip2Fs^tL+p5xrw4e0R9cryNig*Qm99AT2>K`J_ZtV`sYF+WVfz|JC5a< z32|_d#wA0pmA>Jqt>Mq+?{md{55I*UK^tnI4QyYb3M%J`W?RySWV>v6a0W~ptFc#8 z2+k-`_?PuZljI(Z0ycu)f%7=#+3so6_4~q}&izudsVk7dFnzrcH(x^A=H{OU7it9{ zgYY)1WTAuDc>i}P!P>*jVZ-o?Z5mw5N5SWmc^m+(>O|VI_8zn(F&6&QuB}9w1$fl; zoTz0hq;ReN$wnV{J0^Im88if^GkC^Y=)-Cng#lZ*eux-L-x(rbUK01LbXkwx0H)&siZdBsGY zPMq(uI&?)?UC^J;$pV4_0RaDqB(do1D>>Gq+(e`t>wW2jmbau)pXl!~H>zdxJy}70 zFQJ@~IHF5WD^efq<~`Y-ZapdE=Lvf^%`bxIV>8_ zpi17`dW!em{S$qPsQ zzinB>N&dd23n`lgx*LKC_VcD7mz0m(-cw6)yAq_15GEpUr!x{xm4s%+;_{m6IW3%P zUSyYXejLf2KjE=QU6-!scDoFt6`f&l+0&(85wU;4{*%q6qPV8GmC(8`8*ZrUuiq8z;aBT#Uur zDKGrMBOFWEVn`AV4MGdUv>t%IN_x}+7WbM=`KGAls{0t4O86`nN}ZmdaAZh%0M=q= z(+Zh=EN$fYA?`prE2p~xf&l+H9=lOdxG5_)MY;GlVN!q)iERlQWT;`HHkO%rFj6$8Eo2%|Sb z{rF3nAU+bOCGW7&gx08o=COjKnG9OXBv*(*XA_7DIoHpF9f_YHl z6Rf__Wk*KocnrQNJq$tmKXqr5#tV-L-)jzV4sP&t6|KgmadNx7o=1RY_L`XR=my%F zxJ*{_68OcU5|vB!271W?f&l>lq7j)r+-H%^f<*}JSZVtPV&|7V_uzNj4B0wPZU%(I z!-#xJMnItAM}A*#OO?#rh~@QEQ+PsjlRKVqeTyM#dxk0l02_r9_ zBsL~(g-PWYbR%PVYD*$Dua&VxhCu}x`T7lBNtJ2|fQF1p@+xlc!^9lFEq)(oD{-AG zMv3>^$8)&&Cqm9W>bB2ITLy^6lMzc_-U;tIQITU0U)4vV zu^PNP`(&Igf`#YYx+c>R;XC3`YZg5g(tGs#J^3oGTnXfccDQZ1E2^{1-{SxQU()Tj Tbx&XXcZMQG44N=;lHrQ{Y;S%h literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_4096.pem b/libs/nixio/axTLS/ssl/test/axTLS.key_4096.pem new file mode 100644 index 000000000..9929467f4 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.key_4096.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_512 b/libs/nixio/axTLS/ssl/test/axTLS.key_512 new file mode 100644 index 0000000000000000000000000000000000000000..7ae50f23bdc90e48f165aff330220508a99699f5 GIT binary patch literal 321 zcmV-H0lxk)f&o1O0RRF)0MR=zU#S5?J%(*^zCdnK!AXBtb)hXb<-t7_Lcx{!|rfVZ|0=xRXKgPd)!Gy#fOP009C)0Fimb?T$65*W6;Z_#YNk zQ@#a*O7BqLt*OZgemVyg$o&?(z~q?GMVVlAY-DAL#~wAJ%@hSVITaeX4+WM10RkZa z=2Ex*d>@xxG0k=x#?fIqh)t}0wDn9_HXM@#5)1QLVZv(dI{VJ z<#zME>JR1d(>*U^;{HF#AygqJ2JGkYa5*N@$(u= Tmj7@iAR3)zxTTMr$9H6O@syE# literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_512.pem b/libs/nixio/axTLS/ssl/test/axTLS.key_512.pem new file mode 100644 index 000000000..1e2fb41f8 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.key_512.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPQIBAAJBANE7MF+pAUI9hm1yvkBuUcFJf1d1oS025cE9DyAa0SNt+nTSPiOw +cPygat7sQYiE/lQVa2HFFmK4k0HxTz3/Lr0CAwEAAQJBAJF5xO2ONajX3GK2+B8W +VVO+BYNK71DfranJCX46BxXI/Ra7wOSY0UWZYHVsZGWJxx41os0UBTg5FRq4DwWW +AQECIQDo69eo39iQqjwhpAQxatMh2CWYT7gokyu56V+5o2V3fQIhAOX2b+tQxDsB +w0J9UDN6CdwI5XbzveoP5fHTPS9j4rhBAiEA3c+y6Zx6dZHYf8TdRV5QwDtB2iGY +4/L7Qimvwm6Lc1UCIQDDXWrVsocTTjsReJ6zLOHFcjVnqklU2W7T1E8tvKE3QQIh +AMRpCFM7MrS2axuc8/HzGkqW/3AlIBqdZbilj5zHd2R0 +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_aes128.pem b/libs/nixio/axTLS/ssl/test/axTLS.key_aes128.pem new file mode 100644 index 000000000..8961bd9a5 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.key_aes128.pem @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,B3A0D2BCEF4DE916D0BBA30A6885251B + +v8y74AGReaPLmDt6O8wir6hX1Ze8K4fVNkrLqfDMdW5E7jBXKO8riCMNmSjQ9fyh +eTicej93+8krcIvSXKW18TdO+EWezQevgnLrAZQWaNPH2j4B+K5gm701uiiKFKVa +1zngAOByePYlN6z4JLbiCyJRhxSo5zCaUYkKC2eGh8mlE64QmokPSCAj0wcCDzGh +hdhBg1vm0GmaQwIDVn+8zMfahscXVMtBmyQf5YP4PQW2nqOt7aZHjBNdg9qnBpGw +b6YuY7eZ4FgQvYcsNCi34NroJb9pkTrrF2F9Meb6+3So7jtMFG/YaJdCuXtf01g/ +Qm+XA5pJUtIUr/hLQjhkaOVUtXv/k0o/MR4k5CbAmboLt6YHf5V8+01vk0bvv5dI +70pVdXMmx26xDZOGmjYzd93PWc+75jak3GN2fbWryQs= +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.key_aes256.pem b/libs/nixio/axTLS/ssl/test/axTLS.key_aes256.pem new file mode 100644 index 000000000..7671a302f --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.key_aes256.pem @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,F076229CDC2BCB3B8722E3865855B45C + +WFV9QWzr4tNmD+1OeQ7BceQg5LVQHp20Jo1Ax29lq8JTPzeObhtaU2MUHlcPKHUS +vK4FyQxJ25CyMubbnaZqCCz9pNbseFuJ1tob9UqRmXkZ8HV3snRjJRbcctD+V9x+ +Ymi1GreXoDQtMp0FtMiFjPvIYciBQnaRv2ChMAnGXNbZXCxWWA9E5S3a+yWzo+gd +wEcowL+SUac1PEDGHokhKn7nctvI9cC4hE6JmKM1sD68/U3rRPXMGqmC7umqyT5P +gjWBb1uu0iRjFC9eQUsaKPxey5Be710GFlyf/Ff/tep7RhkryIWEPvIzYCBf6rhk +3pysFgTjfiUuBYUNumjXr/q5hgdtb75788XUDxKwAoUx+m8gi0nJg35CN2nmQ054 +VJxcZlNv0wqnJ+GTTZeN6fiAhTpVtHsqHQomRSfaBiw= +-----END RSA PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.noname.p12 b/libs/nixio/axTLS/ssl/test/axTLS.noname.p12 new file mode 100644 index 0000000000000000000000000000000000000000..9d27999fa053e524fa4ca893c2d13ce9ed49bb7c GIT binary patch literal 1483 zcmXqLVm;2p$ZXKW+RMhN)#lOmotKfFaX}Mn9ZM5y6;Qa$poyspA;q?!i76W>l+MJ+ z0HhKSGK>Z?Y+O(ico-QC_*gicZ@+tPGI5?Z6B7qRLlcv+>xsu7BXuG}7WFyS3yN1O zJiB%z_haDaFJk>AMt=^4KDj&FlSO0x2df3KgsbB{=vL6^R#b?e&OS-QcS zGCN$J5|-cmTV%e54S!)2?UI zWu*`8Kl-TagceQVV7aLOe%rD0rav=xK9;Py<7Mxpy7}0RueYwx?_+3MS>c%f$vi$Y z>qPY0e-jNE!}i!EIz_nZJDZx%Fa4AynB5}RZoGR%m9)iXor?9S-~7oa}kp z&%|qA>g?W->CVE3V>Q*gZY|2(~y2WNWEVzf?I z<(U-KKKn^n)b)LfN?Z=|E{NH6jr(|qli}4W=1uv^j~B=u=E~*SW5W}$okLAO`S{IP zm&N{b_Df#*e&uoyfd~wEuSU-w(_`~mv6zJ zXND(%Y3vJ98Uv=jrz}lO4}rpWf%qmHH!Q_*F){%q7Xk(6VkShcPs!1(#%Z>ogb37L z{j#quvpMaUTGqUyQ-7E)zkJ$hUr<88Ndd{X+aJDG=s(t#T=rr1MYoSPrQV;Yy}z+m zy?`~c?A~9^kPHi*)!q-?Fa37Dv_6naZgJ(Ox~e3WYP$wqCBy$MnR+L9v^ugY2xd#-gtrp&?V z%1b_dxY9+VRUwb~ASsUAx z{&$<{bAPTu(cML_!?$=aJkI&ra-Px8)p5DT#2>Fzt$sAlbPs*aYTIDLJE^z&&fe?l z^Sk%Vzox!;X90tFQ`-xh6V7XQZrpS%wK>%Iy6xiU|AVYWX5_w_yT7M+A7@N?Xa9pO zC+yntHF^4$pPv6HK!{B^ccmIv%Zd0HP5-EE{lWK-zxlMywZ@@flle{ob(fXKCWfJZ z_vby)@(Da^SW$QG_ddtyKiO82(qFgbfBiarGH zyBpJ1of*Za&nY)6zS(c*82^(q&7^aotL?qtXSb}Jpn6Gv1;=%GfA;Bu!uO$&LNQUoK`?>=Jpus$0zm-LJ1}3V0YW{7 zZF0UqZc)KWe^+&(EjH!BJr5un(Iaj8bkaT}uyFjKYToQYh=l%B6>DL|7Gk)QLGe#L z|1P}(0|5X50zm+gdBp9GHK^CzVz&4n7FAQe1%pcOP~WYo$q9Zs2NlTu7Q4XYn9)U< zV0CO{Wr@cgHKNTF1vohs8n_PymH`0*Apq#>*Qnpvkg7Z(qy#Z)(;?U;m`}JUlPkID zU%8`YcYOjO0Oj^?>rliy0mDLlP&0Z7+z91%^S$a1{HF-OsY=oO*SU z*nh;`MP5+AJ3-nZnB(&MLMgApZi{nO0wDmyU24^`hZ9aa5qO@nEaAm+HD{_xRM~FR t)K4wEp*KMSAppc_2va*Uw6<#-ob&PX8cLS`a3vrbon^SCkDSMMWONLcmL~uJ literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.unencrypted_pem.p8 b/libs/nixio/axTLS/ssl/test/axTLS.unencrypted_pem.p8 new file mode 100644 index 000000000..e07375a84 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.unencrypted_pem.p8 @@ -0,0 +1,10 @@ +-----BEGIN PRIVATE KEY----- +MIIBVwIBADANBgkqhkiG9w0BAQEFAASCAUEwggE9AgEAAkEA0TswX6kBQj2GbXK+ +QG5RwUl/V3WhLTblwT0PIBrRI236dNI+I7Bw/KBq3uxBiIT+VBVrYcUWYriTQfFP +Pf8uvQIDAQABAkEAkXnE7Y41qNfcYrb4HxZVU74Fg0rvUN+tqckJfjoHFcj9FrvA +5JjRRZlgdWxkZYnHHjWizRQFODkVGrgPBZYBAQIhAOjr16jf2JCqPCGkBDFq0yHY +JZhPuCiTK7npX7mjZXd9AiEA5fZv61DEOwHDQn1QM3oJ3AjldvO96g/l8dM9L2Pi +uEECIQDdz7LpnHp1kdh/xN1FXlDAO0HaIZjj8vtCKa/CbotzVQIhAMNdatWyhxNO +OxF4nrMs4cVyNWeqSVTZbtPUTy28oTdBAiEAxGkIUzsytLZrG5zz8fMaSpb/cCUg +Gp1luKWPnMd3ZHQ= +-----END PRIVATE KEY----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.withCA.p12 b/libs/nixio/axTLS/ssl/test/axTLS.withCA.p12 new file mode 100644 index 0000000000000000000000000000000000000000..ae029dee0fd45e90460d0eacd15f9be1882756ea GIT binary patch literal 2089 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 E1>TCewg3PC literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_1024.cer b/libs/nixio/axTLS/ssl/test/axTLS.x509_1024.cer new file mode 100644 index 0000000000000000000000000000000000000000..fc92d056429c92b14fdc609505a5528a38ddd3da GIT binary patch literal 475 zcmXqLV!Uq9#OT1p$?)-T`++N8!VeqpvT6)$}FigkQ3)MFatt!Lqii2 zW1}c>USnfJ10!Q7*FeWm%s>QUDldwuhC&7cAaQ14PD3MeJp&*#G-#ZU>?%f92Ij_I z27|^t7JSY6*W$;@+`i|$?C)O$;UTG zreV3rM3+B8n;$-Z?C-Lx=WvPT3!e*Dd{h#*I85z1z_p#p(y(l}<|sS2Nm9q#MI#?}#GK1|G;Nj9n?r9a zx*tqPn=Ep8#%g2J%eAqATyu5`M7USYn40q6C$>rQ=PUlWS*MNEQ%>babX_=L_GM?X j$6)$}FigkQ3)MFatt!Lqii2 zW1}c>USnfJ10!Q7*FeWm%s>QUDldwuhC&7cAaQ14PD3MeJp&*#1iDZO*;$ON49rc8 z{0u;GE~X|%MutNR_TT<>zJSeT^Za|gwm0>UnA?6&&rWT6+_6ixVL4aXImM&>R`J}R!t z+jA&U_kPRFCBl9a^}ihQSF@EY+r99L$0v8O9WpZ)@!zhFOWnlzWtp*8&m^W2_0?9l zw)n8tFa2|oB|f!S?S*U#tJV9Eu!NU)c8mVMWhyc4la_L1xyOI0f}@sf*H3S3R=#}F zSLVfo6y=MD+>iH`Cet R9GogVhX2<0z0YoW4gkCo^O*nu literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_2048.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_2048.pem new file mode 100644 index 000000000..1ed0141af --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_2048.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWzCCAcQCCQDxw4fA1PRXxDANBgkqhkiG9w0BAQQFADA0MTIwMAYDVQQKEylh +eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNjA2 +MDcxMTQ0MzJaFw0zMzEwMjMxMTQ0MzJaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl +Y3QxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMKgv9v6z3AGRLOf3o092S/ENz33Z2tlguOIuh2Apwp2ziHFvul7m9iF +xsEcEARcvpkRPVo6ifJLjhJErenvvMAIS3WoO1lyenMaGoNddLeRRB0snRn7xRcl +DYzCYS3fhJmkE06RL/TCTyY9GXa7odRI8kcWuByZog/be15lsgn0pjNKjJICdCer +Otq0TAV/pfzRBF9lcyboHWQFOu9UVmDp3LsV/9o1GJbyKiNZd0j/GnDFOQbXy7GD +I9PJTRzo4GQj0cJHY7JelORKiIsymcoMNRTboFRAx5y9jAGF8Ks196Rq/+9gYsvi +eE0h+pbdLLbM0uZvAYqzIGK9hRR7Ja0CAwEAATANBgkqhkiG9w0BAQQFAAOBgQA8 +L1Zz9K6M/PQCYWrfnTjbPKY2rTB1OvSV0Uwy5KKPQRS1+oK9dx4K0miX+1ZvI1bo +f7/1aFXOsW3dpTwYUSjJvTMjSwNUPKiB/q/xwA1mzsbIZsbnhIITU95mOJ3xFhgc +YFdJ4saL7pppTzfOxZ+h9jWbDwgJJAwx/q+O72uE5w== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_4096.cer b/libs/nixio/axTLS/ssl/test/axTLS.x509_4096.cer new file mode 100644 index 0000000000000000000000000000000000000000..40bbe94fdd8d5fc57dd7463e4e9530b9d661e7c6 GIT binary patch literal 863 zcmXqLVvaUwVmiXa$?)-T`++N8!jBs8vT6)$}FigkQ3)MFatt!Lqii2 zW1}c>USnfJ10!Q7*FeWm%s>QUDldwuhC&7cAaQ14PD3MeJp&*#1iDZO*;$ON49rbT z{0u;GE~X|XMutD?TV}>;ow{Ja^XqY}Kx&Zef|Au{+XOz`xm&ZC_3Bzx*40-wFfQg@ zm%j3gNv(kccj0BVI|(i7yZNjX1&Wq@zCAtZ{?bm~_lpMDeGtww(0oyI-?vNu6 z^TXM09Ex^dDZ;tar^LMBOeK%H7W;SJ#A{)zrj;HmnoStPqxZLgEm3zNIDcdNGvx){gK$*>jXI`gWlo-;Cf zm$Cl}S9azz?Puni3$k++BCg c{uLH|{+cssT4etA3m$>ump}GawD^_-0G~{BVE_OC literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_4096.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_4096.pem new file mode 100644 index 000000000..b7aed1cab --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_4096.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWzCCAsQCCQDxw4fA1PRXxTANBgkqhkiG9w0BAQQFADA0MTIwMAYDVQQKEylh +eFRMUyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNjA2 +MDcxMTQ0MzJaFw0zMzEwMjMxMTQ0MzJaMCwxFjAUBgNVBAoTDWF4VExTIFByb2pl +Y3QxEjAQBgNVBAMTCTEyNy4wLjAuMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAPwntJldKsrQMAz6410QZVIdoHSrNrYQ8NzdfKMF1a0lBavUsAGjDa5n +qfQ0fTBAC3HTJtxghCe7DjohEHKk89uXYt+liQ3vo6Nc+Zw9l2bDyLKffpOdVc/l +tweL7FyQIM1fl2W5r/S9OffFRMJKXiN1HPXrfGYRrwz6B2cgUBtSgI+odwCmdfjb +Ya4Wo8Za53UGeQ7v0eQ0lEDgXAhx3c2YIoXXxfe2J/5KdZnpXtRJWTJaoAbaQ4tU +xECfVwbYwltHqRQJuUx0N4DMeQwnKgf3DWHWVqqWdALsAE5utfuOpuHxSoN2AGDz +ov++GihbiEAcP5XPFv/0ldcfEi/4X9lyHb0mvUIa6DTdHd17thFEM12caD0TCszq +in8JWVu6M0+oRQcDzIfMn4aD7oKtk76gY5HtbN4Jr8E4BHH/oGPe6l4mQl+W4f3o +Zp2e7yG5WBVA9sjJbCmlxiWMHod1iiZUJ4e9cEFINjMWuooPIo6xKMzZcGHzwkBa +zS6kHyTsHcfdHcu4aVMlJ9cKN/aesJgup/XseM3imzuJ3UqfvwCfneUKfM6DM8J3 +mFfSwUBvK8uLsMw5Elim/1oIbBG0PTt7BiLiNd0pU/weuL92y2+QB2oCPM0t5D7L +IYyoZHHNlvTCkQVNsCHgrDUtOdpGlKt6zzEgUTqwC5i5N3p/w6uPAgMBAAEwDQYJ +KoZIhvcNAQEEBQADgYEAcrCtPXmZyPX01uNMh2X1VkgmUn/zLemierou7WD/h7xL +dOl4eeKjFBqIiC19382m1DK4h1F8MceqaMgTueCJpLM7A2cwN3ta8/pGP2yEVhdp +h10PkdRPF/AU8JmxnFaADsc6+6xWbbrdNv5xcvP1bJKWWW+30EhRF9PxjXiETXc= +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_512.cer b/libs/nixio/axTLS/ssl/test/axTLS.x509_512.cer new file mode 100644 index 0000000000000000000000000000000000000000..48c6e13aa0af5678668c7d4a3245a3304030f01e GIT binary patch literal 406 zcmXqLVw_~q_=ky;;p5@<16RI;A2Q%&R#QM#fOCfsUb=fe6G@UKCReg$x8h;>^OFhDPRk20&Zk9;ChBUA P9b0QY{GW6AgzOst>~fQg literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_512.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_512.pem new file mode 100644 index 000000000..8191e489f --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_512.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBkjCB/AIJAPHDh8DU9FfCMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNVBAoTKWF4 +VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA2MDYw +NzExNDQzMloXDTMzMTAyMzExNDQzMlowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVj +dDESMBAGA1UEAxMJMTI3LjAuMC4xMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANE7 +MF+pAUI9hm1yvkBuUcFJf1d1oS025cE9DyAa0SNt+nTSPiOwcPygat7sQYiE/lQV +a2HFFmK4k0HxTz3/Lr0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAKRT6LwFr1xedJ +b4qrvjB+EwV/0p4TNNXUS9S30rMSFvRar7VxvLP1lpYj9PR1JGSZMG/B6hR4yumF +Rjwel9FPgNcWCW4DXAWqz3UQF7oZtJL6K+XJpQ0gwC+Nxc+RRGNLMlK7dLiqFh/V +qZLej5Xy93M0JyZBiLV88P+c08gd7A== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_aes128.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_aes128.pem new file mode 100644 index 000000000..9a75fe960 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_aes128.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBkjCB/AIJAPHDh8DU9FfHMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNVBAoTKWF4 +VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA2MDYw +NzExNDQzMloXDTMzMTAyMzExNDQzMlowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVj +dDESMBAGA1UEAxMJMTI3LjAuMC4xMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMDo +g6K2iXFftW+Qk+rrzkMGWrtfY6YSxPstPRrI7akluUEoyWGITXbK6L3QfERrf2eu +CnWyciQiHVRoHC0EgZUCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBT6YhR8x/bBteK +lr8E0l4mATOnYlsmge+z/SFYs4bDBofqlwQCVJXNSBA4ZsEjgP9qIWTu/85QrVGq +LrkewSM6Oeh95LGnE+uhJVtIX++O+Hsex3H1UL067dCG99XmDhqbEU9AI6YSZu2p +cjoSowFELtOoG667+id9QObfV3EQoQ== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_aes256.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_aes256.pem new file mode 100644 index 000000000..4f3074e01 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_aes256.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBkjCB/AIJAPHDh8DU9FfIMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNVBAoTKWF4 +VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA2MDYw +NzExNDQzMloXDTMzMTAyMzExNDQzMlowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVj +dDESMBAGA1UEAxMJMTI3LjAuMC4xMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANAW +9PdXa5u4gWi5VB5p/eQmOtteRq9/54JkiEs8cVNrTQgZsjjU1LGedE3JwBqZ1EIW +HGPjcGg5dVxFjkn7RekCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBmJMt0Crdd/BPn +EdmzsVXou0zTizTC8wyUPMVpg/KzzP7fhZux/ZIrH9/RVcJd9y+B2/mXc3C+K99+ +TXQoYKsLGArfDPzmpy1wPrdEcB1A9gkWDl1Uq6xRyvrVm3gX8NTITRuGKL9njgWx +2SrApIBtOOUOinYtfH3745cVVl5HOA== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_after.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_after.pem new file mode 100644 index 000000000..79eb9ccd6 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_after.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBkjCB/AIJAPHDh8DU9FfKMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNVBAoTKWF4 +VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA2MDYw +NzExNDQzMloXDTA1MDYwNzExNDQzMlowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVj +dDESMBAGA1UEAxMJMTI3LjAuMC4xMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANE7 +MF+pAUI9hm1yvkBuUcFJf1d1oS025cE9DyAa0SNt+nTSPiOwcPygat7sQYiE/lQV +a2HFFmK4k0HxTz3/Lr0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCmPSs9EceViMZD +ZTXDZpQWJFcXaeInrXWgYWyVgnHBY/eSuqNCxkV/ehv/Wc5pWBGnrX+4cSvQ+TpQ +FdZegeOjvgipjtJb/0TJCcvgcdHTntEM0h7VXjfbsJXAHwJPFzWIKxV4jeFXnaaw +W+YHrj9GQ8PnFmapPuh4h/y6LyHAcg== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_before.pem b/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_before.pem new file mode 100644 index 000000000..fe72b541b --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/axTLS.x509_bad_before.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBkjCB/AIJAPHDh8DU9FfJMA0GCSqGSIb3DQEBBQUAMDQxMjAwBgNVBAoTKWF4 +VExTIFByb2plY3QgRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTI0MTIz +MTE0MDAwMFoXDTI1MTIzMTE0MDAwMFowLDEWMBQGA1UEChMNYXhUTFMgUHJvamVj +dDESMBAGA1UEAxMJMTI3LjAuMC4xMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANE7 +MF+pAUI9hm1yvkBuUcFJf1d1oS025cE9DyAa0SNt+nTSPiOwcPygat7sQYiE/lQV +a2HFFmK4k0HxTz3/Lr0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQApbldYefE8A0ez +SYvAuCtYxx/2KHwBRD/cR0q7widl9WGjVC/dsnbFo109vHEr3FP1HVYSI0aweiaK +XZmpUyJ9DprbbWQqaLuDnqIH8X7kfiMuO7/LGQc812iDJI2Akxp9cIlPBFBD8GVx ++0EphzSodDDlLD8bPqLaWTE+8Ydtjw== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/axTLS.x509_device.cer b/libs/nixio/axTLS/ssl/test/axTLS.x509_device.cer new file mode 100644 index 0000000000000000000000000000000000000000..c966743c9ca724ecb81928d0f6ad7e62b29eab92 GIT binary patch literal 401 zcmXqLV(c|&Vl-#sWcYZv{lJwk;l~Vk**LY@JlekVGBUEVG8pI>iW!KoF^95n3G*ga zg!lw21Qg|Gr6!jc3KRT4UNq841mzkKu(<3zzhh@4Gm39jE$nid5w(?4UCMT zTmx-GO#^j^X$mN&DY>WhSR8IHwksWTs^%CzhldG|oqM3?nN8b7L=qL1QOVVA zB|csu?fqp62d3^8%Qa9mSYy#4{w;o%qQvxI>(vh>*4W)IqNC%Ho9fI z%iCXCtE2tbBwl5l92 +#include +#include "ssl.h" + +int main(int argc, char *argv[]) +{ + bigint *m1, *m2, *d; + BI_CTX *ctx = bi_initialize(); + char cmp1[1024], cmp2[1024]; + + const char *plaintext = /* 128 byte number */ + "01aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee" + "01aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee"; + d = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + memset(cmp1, 0, sizeof(cmp1)); + + while (1) + { + bi_set_mod(ctx, bi_clone(ctx, d), 0); + m1 = bi_square(ctx, bi_copy(d)); + m2 = bi_residue(ctx, m1); + bi_free_mod(ctx, 0); + + //bi_export(ctx, bi_copy(d), cmp1, sizeof(cmp1)); + bi_export(ctx, m2, cmp2, sizeof(cmp2)); + + if (memcmp(cmp1, cmp2, sizeof(cmp1)) != 0) + { + printf("Error!\n"); TTY_FLUSH(); + break; + } + + d = bi_add(ctx, d, int_to_bi(ctx, 1)); + } + + bi_free(ctx, d); + bi_terminate(ctx); + printf("all good\n"); TTY_FLUSH(); + return 0; + +} + diff --git a/libs/nixio/axTLS/ssl/test/datatest.c.old b/libs/nixio/axTLS/ssl/test/datatest.c.old new file mode 100644 index 000000000..a5703fb9e --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/datatest.c.old @@ -0,0 +1,280 @@ +#include "crypto.h" + +#include +#include +//#define DEBUG_TEST + +typedef enum { + encrypt, decrypt +} CryptoMode; + +void hex_dump(const char* header, const unsigned char* data, const unsigned int data_length) +{ + unsigned int byte_count; + printf("%s (%d bytes):\n", header, data_length); + for(byte_count = 0; byte_count < data_length; ++byte_count) + { + printf("%02X", data[byte_count]); + } + printf("\n"); +} + +void do_rsa(const CryptoMode crypto_mode, + const unsigned char* data, const unsigned int data_length, + const unsigned char* modulus, const unsigned int modulus_length, + const unsigned char* exponent, const unsigned int exponent_length, + unsigned char* result, const unsigned int result_length) +{ + RSA_CTX* rsa_context = NULL; + BI_CTX *bi_ctx; + bigint *plaintext_bi; + bigint *enc_data_bi, *dec_data_bi; + +#ifdef DEBUG_TEST + printf("do_rsa:\n"); + hex_dump("data", data, data_length); + hex_dump("modulus", modulus, modulus_length); + hex_dump("exponent", exponent, exponent_length); +#endif + + RSA_priv_key_new(&rsa_context, modulus, modulus_length, exponent, exponent_length, exponent, exponent_length); + memset(result, 0, result_length); + bi_ctx = rsa_context->bi_ctx; + + switch(crypto_mode) + { + case encrypt: +#ifdef DEBUG_TEST + printf("encrypt\n"); +#endif + plaintext_bi = bi_import(bi_ctx, data, data_length); + enc_data_bi = RSA_public(rsa_context, plaintext_bi); + bi_export(bi_ctx, enc_data_bi, result, result_length); + break; + + case decrypt: + +#ifdef DEBUG_TEST + printf("decrypt\n"); +#endif + plaintext_bi = bi_import(bi_ctx, data, data_length); + dec_data_bi = RSA_private(rsa_context, plaintext_bi); + bi_export(bi_ctx, dec_data_bi, result, result_length); + break; + } +#ifdef DEBUG_TEST + hex_dump("result", result, result_length); +#endif + + RSA_free(rsa_context); +} + +void test_matching(char* test_description, + const unsigned char* expected, const unsigned int expected_length, + const unsigned char* result, const unsigned int result_length) +{ + int test_result = memcmp(expected, result, expected_length); + printf("Testing %s ... ", test_description); + if(test_result == 0) + { + printf("ok.\n"); + } + else + { + printf("failed!\n"); + hex_dump("should be", expected, expected_length); + hex_dump("but is", result, result_length); + } +} + +void encrypt_decrypt_should_yield_original(char* test_description, + const unsigned char* data, const unsigned int data_length, + const unsigned char* modulus, const unsigned int modulus_length, + const unsigned char* private_exponent, const unsigned int private_exponent_length, + const unsigned char* public_exponent, const unsigned int public_exponent_length, + const unsigned char* cryptogram, const unsigned int cryptogram_length) +{ + const unsigned int calculated_cryptogram_length = modulus_length; + unsigned char* calculated_cryptogram = malloc(calculated_cryptogram_length); + const unsigned int decrypted_data_length = modulus_length; + unsigned char* decrypted_data = malloc(decrypted_data_length); + + printf("\nRunning \"%s\" ...\n", test_description); + +#ifdef DEBUG_TEST + printf("encrypt_decrypt_should_yield_original:\n"); + hex_dump("data", data, data_length); + hex_dump("modulus", modulus, modulus_length); + hex_dump("private_exponent", private_exponent, private_exponent_length); + hex_dump("public_exponent", public_exponent, public_exponent_length); + hex_dump("cryptogram", cryptogram, cryptogram_length); +#endif + + do_rsa(encrypt, data, data_length, + modulus, modulus_length, + private_exponent, private_exponent_length, + calculated_cryptogram, calculated_cryptogram_length); + +#ifdef DEBUG_TEST + hex_dump("calculated_cryptogram", calculated_cryptogram, calculated_cryptogram_length); +#endif + + if(cryptogram != NULL) + { + test_matching("cryptogram", cryptogram, cryptogram_length, + calculated_cryptogram, calculated_cryptogram_length); + } + + do_rsa(decrypt, calculated_cryptogram, calculated_cryptogram_length, + modulus, modulus_length, + public_exponent, public_exponent_length, + decrypted_data, decrypted_data_length); + + test_matching("decrypted plaintext", data, data_length, + decrypted_data, decrypted_data_length); + + free(calculated_cryptogram); + free(decrypted_data); +} + +/* configure without CRT! + + prepare data with: + > echo "" | + ruby -ne '$_.gsub!(/ /, "").scan(/../).each_with_index \ + { |b, i| print "\"\n\"" if i % 16 == 0; print "\\x" + b;}' */ +int main(int argc, char *argv[]) +{ +#if 0 + unsigned char stuff[] = { + 0x22, 0x33, 0x44, 0x81, + 0xF1, 0xFF, 0xAA, 0xBB, + 0xCC, 0xDD, 0xEE , 0x01, + 0x45, 0x44, 0xfa, 0x8d, + 0xfa, 0x20, 0x99, 0xFF, + 0xab, 0xda, 0xac, 0x40 }; + unsigned char resA[sizeof(stuff)*2], resB[sizeof(stuff)*2]; + + BI_CTX *bi_ctx = bi_initialize(); + bigint *bi_data1, *bi_data2, *res1, *res2; + bi_data1 = bi_import(bi_ctx, stuff, sizeof(stuff)); + bi_data2 = bi_import(bi_ctx, stuff, sizeof(stuff)); + + res1 = bi_multiply(bi_ctx, bi_copy(bi_data1), bi_copy(bi_data2)); + res2 = bi_multiply(bi_ctx, bi_data1, bi_data2); + bi_print("MULTIPLY", res1); + bi_print("SQUARE", res2); + bi_export(bi_ctx, res1, resA, sizeof(resA)); + bi_export(bi_ctx, res2, resB, sizeof(resB)); + if (memcmp(resA, resB, sizeof(resA))) + printf("OUCH - difference!\n"); + bi_terminate(bi_ctx); + + exit(0); +#endif + encrypt_decrypt_should_yield_original("Works only with Montgomery", + (const unsigned char*) /* data */ + "\xBC\xD3\x12\x6C\x93\x13\x14\x4C\x00\x5D\xFD\xBF\xDE\xE4\xD3\x60" + "\x29\xB8\xAE\x47\xBE\x0B\xB6\x0A\x39\x88\xB7\x93\x19\x14\xE8\x88" + "\x4A\xDE\x00\x46\x89\x5A\x11\x1A\xC4\x8F\xE8\xF7\x27\xAC\x59\x80" + "\x03\xC1\x93\x14\x01\x00\x93\x15\x07\x00\x00\x00\x01\x01\x05\x20" + "\x93\x16\x0F\x42\x34\x33\x3A\x58\x30\x30\x30\x31\x30\x31\x30\x30" + "\x30\x31\x92\x6B\x10\x6C\x69\x62\x65\x6C\x6D\x65\x74\x72\x65\x65" + "\x2E\x73\x6F\x2E\x30\x93\x18\x02\xA5\x92\x92\x6C\x03\x96\xE3\x0C" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xB7\xBE", 128, + (const unsigned char*) /* modulus */ + "\xc4\x5a\xcb\x35\x95\xad\x32\x4a\xcf\x9c\x82\x45\x13\xb7\x42\x35" + "\x22\x32\x6d\x2e\x6d\x26\x2e\x6d\x00\x9b\xae\x2d\x9e\x78\x1e\xdd" + "\x40\x23\x17\xa8\xbb\xa1\x07\x86\xb4\x3c\xbc\xe8\xd5\xfc\xd9\xeb" + "\x3c\xad\x63\x11\xf3\x1d\x64\x81\x96\xf2\xf5\xfe\xca\x5a\xf7\x8a" + "\x15\xcb\x90\x81\x68\xae\x59\xb4\xe1\xa4\x41\x99\xcd\xf3\x98\xbd" + "\x3c\x48\x37\xdb\xa1\xc3\x1c\x6f\x43\xd1\x89\x23\xe5\x3d\xa3\xa5" + "\x92\x7b\x19\x14\x1e\x7a\xf3\x88\x8a\x36\x21\x3e\x16\x40\x3c\xd7" + "\xd3\xdb\x13\xaf\xc9\x68\x45\x84\xb3\x39\x8f\x02\xed\x28\x02\x5f", 128, + (const unsigned char*) /* private exponent */ + "\x5d\x19\xb7\xb4\x66\x8d\xc2\x84\xda\x3f\x99\x3c\xeb\x86\x3e\xec" + "\x36\x94\xb6\x54\x07\x08\xcd\x86\x7d\x7d\x53\x6e\xe9\xee\x86\xa3" + "\xdd\x5f\x46\x3e\x89\x08\x67\x2b\x25\x96\x8e\xf3\xcf\x52\x9e\x78" + "\xfd\x42\x30\xf1\x37\xd6\xbd\xea\xfc\x09\xa3\x3d\xf5\xf0\x7f\xe1" + "\xb1\xe0\x69\x13\x44\xf9\x8b\x95\x58\x2a\x81\xb3\xa8\x15\xce\x7e" + "\xd3\xea\x97\x0a\xa2\x14\xd4\xae\xc7\x75\xbb\x9f\x68\xa5\x53\x0e" + "\x85\x29\x88\x48\x6c\xc9\xcc\xde\x72\x40\x3a\x4c\x82\xde\x3c\xfb" + "\x08\xf8\x2c\x26\xb5\xd4\xea\xc4\xca\x98\x6e\x43\x3e\x67\x54\xc1", 128, + (const unsigned char*) /* public exponent */ + "\x01\x00\x01", 3, + (const unsigned char*) /* precalculated encrypted data */ + "\x93\xE8\x1F\xF9\x70\xFA\xAA\xED\x54\xFD\x48\x37\xC9\x71\x9A\x11" + "\x69\x80\xB4\x22\x0C\xAD\x5A\x95\x65\xCA\x7C\xF7\x70\x56\x92\xCB" + "\x45\x6D\x58\x84\x21\x80\x23\x76\x21\x4A\x61\x99\xC1\x11\x9C\x0F" + "\x40\xED\x80\x9C\x8F\x3A\x4F\x01\xB5\x72\xC3\x24\xAE\xF3\x6B\x98" + "\xA8\x60\xAC\xAF\x95\x98\x9A\xAA\xA4\x28\xF2\x02\x05\xFC\xF3\xDD" + "\xB0\x5A\x4E\xDE\x3C\x41\x4B\x1C\x5B\x1F\xF6\x3D\xAF\x93\x43\xCB" + "\xD8\xC7\x24\x97\x8F\x49\xE5\x5B\x10\x51\x3B\x1E\xA6\x39\xEA\x4E" + "\xA5\xE0\x71\x8C\xCA\x34\x8C\x2F\x6C\x5C\x78\x34\x86\x7C\x54\x6A", 128); + + encrypt_decrypt_should_yield_original("Works only with Barrett", + (const unsigned char*) /* data */ + "\x36\x42\x32\xe4\x1e\x78\x02\x8e\xfb\x64\x5f\x0c\xfc\x5a\xd7\x5c" + "\xe4\xb5\x91\x5c\x4b\x00\x87\x28\x87\x9b\xa0\x4b\x09\xc2\x6b\x64" + "\xac\x4b\xcf\xa5\xee\x8a\xb7\xc9\xc9\x90\x02\xc1\xa3\x47\x5c\x6b" + "\x71\x5d\x5d\x49\x27\xe1\x15\xc6\xcf\x37\x9e\xa7\x0f\xa1\xad\x96" + "\x83\xef\x4b\x53\x68\xcd\x77\xfc\x14\x5f\xf5\xb7\x78\xb0\x10\xeb" + "\x0d\x61\x94\x01\xf6\xaa\x1b\x19\x23\x39\xa7\xcc\x6c\x42\x4a\x87" + "\x79\x27\x04\xc6\xec\x8e\x50\xba\xb9\x26\x89\xd4\x00\x01\x25\xe5" + "\xf3\x9e\x98\x0c\x8d\x2e\x43\x1e\xe9\x29\x90\xd2\x75\x61\x85\xe7", 128, + (const unsigned char*) /* modulus */ + "\x37\x0c\x32\xe4\x1e\x78\x02\x8e\xfb\x64\x5f\x0c\xfc\x5a\xd7\x5c" + "\xe4\xb5\x91\x5c\x4b\x00\x87\x28\x87\x9b\xa0\x4b\x09\xc2\x6b\x64" + "\xac\x4b\xcf\xa5\xee\x8a\xb7\xc9\xc9\x90\x02\xc1\xa3\x47\x5c\x6b" + "\x71\x5d\x5d\x49\x27\xe1\x15\xc6\xcf\x37\x9e\xa7\x0f\xa1\xad\x96" + "\x83\xef\x4b\x53\x68\xcd\x77\xfc\x14\x5f\xf5\xb7\x78\xb0\x10\xeb" + "\x0d\x61\x94\x01\xf6\xaa\x1b\x19\x23\x39\xa7\xcc\x6c\x42\x4a\x87" + "\x79\x27\x04\xc6\xec\x8e\x50\xba\xb9\x26\x89\xd4\x00\x01\x25\xe5" + "\xf3\x9e\x98\x0c\x8d\x2e\x43\x1e\xe9\x29\x90\xd2\x75\x61\x85\xe7", 128, + (const unsigned char*) /* private exponent */ + "\x16\x3a\x76\xd2\x66\xfb\x4f\x0d\x2d\xb6\x7a\x2b\x64\x3b\xca\x7b" + "\x58\x5f\x79\x33\x2b\x96\x2a\xfd\xd2\xc4\xa5\x15\xa7\xfb\x3a\x22" + "\x8c\xf0\x90\x09\x11\x2a\x32\xcc\xe8\xf7\x9e\x25\x53\x29\x9d\xc8" + "\x45\x1e\xce\x6c\x9c\x0d\xe8\x1d\x3f\xcf\xd5\xe0\xe0\x0f\x09\x69" + "\x2d\xe7\xd5\xe6\xe5\x10\xd9\x4e\x20\xdb\xbd\xa1\x04\x6b\xe6\x1d" + "\x4c\x79\x28\x47\x30\x11\xde\x14\xb4\x6e\x35\x98\x38\x50\x44\x82" + "\xbd\xc4\xfb\x03\xb3\xf6\x5e\x5a\x29\xfa\x29\xaa\xde\xe4\xfd\x15" + "\xbe\xed\x4f\x93\x9d\x0d\x29\xe8\xd7\xa3\xf4\x18\xc8\x98\xb1\x01", 128, + (const unsigned char*) /* public exponent */ + "\x01\x00\x01", 3, + NULL, 0); + + encrypt_decrypt_should_yield_original("Works always", + (const unsigned char*) /* data */ + "\xB9\x42\x32\xe4\x1e\x78\x02\x8e\xfb\x64\x5f\x0c\xfc\x5a\xd7\x5c" + "\xe4\xb5\x91\x5c\x4b\x00\x87\x28\x87\x9b\xa0\x4b\x09\xc2\x6b\x64" + "\xac\x4b\xcf\xa5\xee\x8a\xb7\xc9\xc9\x90\x02\xc1\xa3\x47\x5c\x6b" + "\x71\x5d\x5d\x49\x27\xe1\x15\xc6\xcf\x37\x9e\xa7\x0f\xa1\xad\x96" + "\x83\xef\x4b\x53\x68\xcd\x77\xfc\x14\x5f\xf5\xb7\x78\xb0\x10\xeb" + "\x0d\x61\x94\x01\xf6\xaa\x1b\x19\x23\x39\xa7\xcc\x6c\x42\x4a\x87" + "\x79\x27\x04\xc6\xec\x8e\x50\xba\xb9\x26\x89\xd4\x00\x01\x25\xe5" + "\xf3\x9e\x98\x0c\x8d\x2e\x43\x1e\xe9\x29\x90\xd2\x75\x61\x85\xe7", 128, + (const unsigned char*) /* modulus */ + "\xB9\x77\xEC\x83\x95\xAF\xB1\xF8\x21\x21\xFF\x05\x5E\x0C\x91\x0C" + "\x2E\xD5\xD2\x94\x1C\x38\x5E\xED\x5A\xCF\x84\xD0\x12\x8B\xAA\x4B" + "\x3A\x63\x65\x78\x13\xED\x24\x4E\x83\xF2\xF5\x02\x66\x5D\xFC\xC1" + "\x80\x5B\x78\x78\xB4\x0B\x45\xE5\x22\xC6\xCD\xEB\xCC\x74\x0B\x0B" + "\xD8\x8B\x91\x99\x48\x8E\x74\xA9\xD0\x1A\x39\x94\xC2\xD4\x2E\x9A" + "\x8C\x0C\x35\x0D\x97\x8F\xC4\x62\x20\xE9\x78\x40\x97\x05\x98\xE6" + "\x22\x48\x3D\x3D\xCA\x6A\x3F\xEF\xB0\x23\x14\x30\xDA\x35\x46\x65" + "\x55\xEF\xEB\xA1\xA9\xCF\x83\xE7\xEF\xF2\x83\x6D\x38\xEA\x88\xED", 128, + (const unsigned char*) /* private exponent */ + "\x52\x2A\x68\xE3\x9A\xAA\xED\xA3\x49\xBA\x6F\xEA\x86\xD1\xF6\x68" + "\x79\x4F\x4D\x2D\x44\x9B\x4C\xA2\xC6\xBA\x6C\xD2\x69\x84\xEA\x7A" + "\xCD\x71\x3F\x80\xC5\x03\x28\x34\x88\x8C\x58\x33\x29\xFA\xB5\x81" + "\x5C\x46\x29\xC6\xFF\xAC\x86\xD8\x8E\x61\x98\xD4\xC0\x0D\x20\xDE" + "\xEB\x61\x1C\x0C\x3C\x19\xA3\x75\x10\x7D\xDA\xA9\x55\xA7\x64\x5F" + "\xE0\xB6\x35\x62\x00\xD9\xD2\xF7\xA4\xDF\x85\xFF\xDF\x86\x75\x29" + "\x66\x16\x03\x8C\xC0\xB0\x3F\xAB\xBA\x41\xB3\x3C\x76\x58\xB6\xE2" + "\x1F\x36\x47\x5F\x1F\x0E\x4C\xB5\x29\x90\xDC\xA1\xF8\xFA\x58\x19", 128, + (const unsigned char*) /* public exponent */ + "\x01\x00\x01", 3, + NULL, 0); + + return 0; +} diff --git a/libs/nixio/axTLS/ssl/test/deutsche_telecom.x509_ca b/libs/nixio/axTLS/ssl/test/deutsche_telecom.x509_ca new file mode 100644 index 0000000000000000000000000000000000000000..0f4b96a0d5b66ac34b258e34a8943630d55feacc GIT binary patch literal 670 zcmXqLVwz>p#KgRSnTe5!iBZLXmyJ`a&7xk*W}qnv{YLwX`%aH?TA`G&V6Xh!W>D1apmyO)Y`kfpAje zeB^LpWMyD(>}4=$>||7N?y$<5%!OzF zERzTg``9Ao-X*XBQfdA{s)V1MQHyO zWy#)aHLqswC)*oOrXGIdcj#m4Y98-5?oH8;9tM=&_?EauBId2}0<I-~KIoDw&uW z85kEk890CgUzU$Wj73Dm@KODoQ$kaJe=JbA?W-YN=#+TOfFBt2vcfDJ25gLs|CtzB z4fsF;{6G#13o!QCki7_udS;;C=E@5EntVbwMZq<0PCjey;-LEFE80^xr_VBLt0)o^ z{eRuHSc9>xWA4OP_pWJj^Vlt(S>nKR=8eFEls^UAc3BFke~M!W*vfs)>;B68FPk|v z8vb08JMyN$t<~qA>juWD7Hh=jyLr9SJ-zpI>PD-l%e+~<`HcU|RG(;Raeo+<;^qYa DuXy4z literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/equifax.x509_ca b/libs/nixio/axTLS/ssl/test/equifax.x509_ca new file mode 100644 index 0000000000000000000000000000000000000000..79b0a3f98764a07fdf6cae1a80eebf704d1f8796 GIT binary patch literal 646 zcmXqLVrnvIVtl=TnTe5!iIK&CmyJ`a&7A-4f18*?ZNn=n&ou%V2B z6o|tmEbLlXnwgeZp%9#!Tw0W>;F*`KXQ*bN0+MAGmPM0Ibt)~+%u6jUR&aLIH8hYD z=e4vnFf%eVFfjmvC~;mR10*hOENz^R>^DYM2Ij_I27|^_GV!Yaq78vdHv|ahSG);D}~qW z>v(UKw>nj0}v6(+pA!1lc$f+B_KBemDWcpM{x;f!%-~7^bqq zjEw(TSPhtglz}`*K$%6tK&(N;tHLNq@OGq_S={}DB4!R*?lnFO4P-$I_*lePuuG#x zF*DHrrO_*9GYdX@8Rm2L=SrUgyt6)^NzHtwT6QLK`?Zk6vS(+mk-WR5_38uLKS>J9 zTf8GuKR)_7Rnh+ImAVKy_x2FGh=p6X{?K|~_^nW3?Pk)dG}kZXa&H83}nHjo5q@oeRF+y~C}AK5 zF_#}?pk8`jX-Q78UVc%!LE{``H!w0c_AnSUb}%(IT;CP5elgDny_M==tAhJhysmF7 z^~s*G&+FB8xgXw#vyR;2W!Uk`M_ArWZ1TOL_s5pRZOYqrimM~Ebi%Z^%fk2YSeNlm zm)raDP5tGSZ+^F?EXZEqajv|sc#fg)f7=u5mL}xg+}0B{D}UR*`B{6He^_9w{&lI0t~bUk(V|yzt>L6d&agTOlQ%<1HtMiK21dqF;?crMuy~AZ)cCC zcar#4F@K*Wyr$(`=edOf)X_qG#W2@k=EugP0m@R{;Q^*WZi) literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/header_issue.dat b/libs/nixio/axTLS/ssl/test/header_issue.dat new file mode 100755 index 0000000000000000000000000000000000000000..a48d23d2b937820a533bc1394d6c155fd28c1959 GIT binary patch literal 1159 zcmWe*W@Kq%Vqj2XW^_B{aBjXp>E)wm*3`MA7YKbx?VUNb&ow+D@R^CtrU!`(45kd+ z3@iZ*EWQj(j0R1Ne}Q;86SDvVivceir&gOs+jm|@Mpjk^gU02C@&>YO%%Ln?!eYS% znMJ92B?`{@xw)lznaPPInfZCehI|G*AO+mQtO1U?;Q@xK2Fh?lI2pynic1R$@{39w ziXqyQ^K_r&^DUdsvg@wz@%k`ix z(#=UO2Kme&200WNSs9qU84Ns_92wpo`5|5!qql9!X6NU%!l&1>n0uS&D5^(1N_N=q zT3#1Xo$~t3DZM?rcUCRjoTpWPC&fDJUdopHOj#EUEFSSFWs-_7#%qy;W$R;IY>2#oGL(LO(Mop($Bszg1H5j-<7 zYyB}vW~D>tw?BFLXek4en?Vzk6EF?DVd7-C`OWT+eQSgcnmt1@C72qS8d)Mz0)#t+ ze1a<>?d5STc=(BH&V_n5t}ZuuwkKi#Kd5eDJK@OGejq5*ct+H>r89oKDBiP7sKoTQ zuBR~j#nTEypxQCB{fdK$z?p#a& literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/killopenssl.sh b/libs/nixio/axTLS/ssl/test/killopenssl.sh new file mode 100755 index 000000000..17950fbae --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/killopenssl.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ps -ef|grep openssl | /usr/bin/awk '{print $2}' |xargs kill -9 diff --git a/libs/nixio/axTLS/ssl/test/make_certs.sh b/libs/nixio/axTLS/ssl/test/make_certs.sh new file mode 100755 index 000000000..dfc39d4f5 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/make_certs.sh @@ -0,0 +1,174 @@ +#!/bin/sh + +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +# +# Generate the certificates and keys for testing. +# + +PROJECT_NAME="axTLS Project" + +# Generate the openssl configuration files. +cat > ca_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Dodgy Certificate Authority +EOF + +cat > certs.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME + CN = 127.0.0.1 +EOF + +cat > device_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Device Certificate +EOF + +# private key generation +openssl genrsa -out axTLS.ca_key.pem 1024 +openssl genrsa -out axTLS.key_512.pem 512 +openssl genrsa -out axTLS.key_1024.pem 1024 +openssl genrsa -out axTLS.key_2048.pem 2048 +openssl genrsa -out axTLS.key_4096.pem 4096 +openssl genrsa -out axTLS.device_key.pem 1024 +openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512 +openssl genrsa -aes256 -passout pass:abcd -out axTLS.key_aes256.pem 512 + +# convert private keys into DER format +openssl rsa -in axTLS.key_512.pem -out axTLS.key_512 -outform DER +openssl rsa -in axTLS.key_1024.pem -out axTLS.key_1024 -outform DER +openssl rsa -in axTLS.key_2048.pem -out axTLS.key_2048 -outform DER +openssl rsa -in axTLS.key_4096.pem -out axTLS.key_4096 -outform DER +openssl rsa -in axTLS.device_key.pem -out axTLS.device_key -outform DER + +# cert requests +openssl req -out axTLS.ca_x509.req -key axTLS.ca_key.pem -new \ + -config ./ca_cert.conf +openssl req -out axTLS.x509_512.req -key axTLS.key_512.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_1024.req -key axTLS.key_1024.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_2048.req -key axTLS.key_2048.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_4096.req -key axTLS.key_4096.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_device.req -key axTLS.device_key.pem -new \ + -config ./device_cert.conf +openssl req -out axTLS.x509_aes128.req -key axTLS.key_aes128.pem \ + -new -config ./certs.conf -passin pass:abcd +openssl req -out axTLS.x509_aes256.req -key axTLS.key_aes256.pem \ + -new -config ./certs.conf -passin pass:abcd + +# generate the actual certs. +openssl x509 -req -in axTLS.ca_x509.req -out axTLS.ca_x509.pem \ + -sha1 -days 10000 -signkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_512.pem \ + -sha1 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_1024.req -out axTLS.x509_1024.pem \ + -sha1 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_2048.req -out axTLS.x509_2048.pem \ + -md5 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_4096.req -out axTLS.x509_4096.pem \ + -md5 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_device.req -out axTLS.x509_device.pem \ + -sha1 -CAcreateserial -days 10000 \ + -CA axTLS.x509_512.pem -CAkey axTLS.key_512.pem +openssl x509 -req -in axTLS.x509_aes128.req \ + -out axTLS.x509_aes128.pem \ + -sha1 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_aes256.req \ + -out axTLS.x509_aes256.pem \ + -sha1 -CAcreateserial -days 10000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem + +# note: must be root to do this +DATE_NOW=`date` +if date -s "Jan 1 2025"; then +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_bad_before.pem \ + -sha1 -CAcreateserial -days 365 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +date -s "$DATE_NOW" +touch axTLS.x509_bad_before.pem +fi +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_bad_after.pem \ + -sha1 -CAcreateserial -days -365 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem + +# some cleanup +rm axTLS*.req +rm axTLS.srl +rm *.conf + +# need this for the client tests +openssl x509 -in axTLS.ca_x509.pem -outform DER -out axTLS.ca_x509.cer +openssl x509 -in axTLS.x509_512.pem -outform DER -out axTLS.x509_512.cer +openssl x509 -in axTLS.x509_1024.pem -outform DER -out axTLS.x509_1024.cer +openssl x509 -in axTLS.x509_2048.pem -outform DER -out axTLS.x509_2048.cer +openssl x509 -in axTLS.x509_4096.pem -outform DER -out axTLS.x509_4096.cer +openssl x509 -in axTLS.x509_device.pem -outform DER -out axTLS.x509_device.cer + +# generate pkcs8 files (use RC4-128 for encryption) +openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 +openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 +openssl pkcs8 -in axTLS.key_512.pem -nocrypt -topk8 -out axTLS.unencrypted_pem.p8 +openssl pkcs8 -in axTLS.key_512.pem -nocrypt -topk8 -outform DER -out axTLS.unencrypted.p8 + +# generate pkcs12 files (use RC4-128 for encryption) +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_with_CA" -out axTLS.withCA.p12 -password pass:abcd +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_without_CA" -out axTLS.withoutCA.p12 -password pass:abcd +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -out axTLS.noname.p12 -password pass:abcd + +# PEM certificate chain +cat axTLS.ca_x509.pem >> axTLS.x509_device.pem + +# set default key/cert for use in the server +xxd -i axTLS.x509_1024.cer | sed -e \ + "s/axTLS_x509_1024_cer/default_certificate/" > ../../ssl/cert.h +xxd -i axTLS.key_1024 | sed -e \ + "s/axTLS_key_1024/default_private_key/" > ../../ssl/private_key.h diff --git a/libs/nixio/axTLS/ssl/test/microsoft.x509_ca b/libs/nixio/axTLS/ssl/test/microsoft.x509_ca new file mode 100644 index 0000000000000000000000000000000000000000..b90803452b6cd92749d3e34c60420fd20e5b1c4f GIT binary patch literal 1046 zcmXqLVi7WEV*0g!nTe5!iJ##hL${4hhu}rKZ^>`&I~ee?acZ@Bw0-AgWMpAwFeot8 zHqc~a4rSpMR&vfSs4U7%&nQvQNY+#^w6ru=@Xbsv$}i4OD^YOHFDlS8lrxZlC>BMP z$uCMQ$;{6)R5XwW$ukQ}AmoGc^Gg&QOG`5Hi!w_p4dldmEzJ!K4Gj#;4S*m@oY%;} z(8$;j${hd~H8Cn72MZ%B19KB2KZ8LNBNtN>BO}8~ro9IXo>_nLlh&9&y&}%w)uNTX zs_{SmP4!v4{omY03U)Pti)JtCPcX{9=#*0VZcW+AkXxT#&uNqCpWl+U`Dk`klJYfK zn|u6>lU__avR^lC)~pP*nHQ{d_Va9wP&?@MoXgH)n$AiM%N1`to1GrF@b8|L6PmcD zsb*n!o!TfGUH;@z+EkjRPZ-kzwi!?gPD_ zr%RZ=-NC4Gqg?QFOoGw;dEBS%SSNP$pR{_W_~}FVWSy-G+)pIE|8xC}wRO}=vyjYx zD!KM2&#bz~T65R1*LJ=@LDuenLjDq&2mV}eek>TJ;QuPAdi^TN6WZpgy8BWW3U#US zColdUBJ)%6pZD?vZDNP*#d?OS@s7n znRPvI6k1`LC7pHK^x+j5mM1G*Sx!V5Tjcr5@Fs2NTGVs-YKXntS((sf1-FH)+9#LJ IUw&x;05YnPSO5S3 literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/ssl/test/microsoft.x509_ca.pem b/libs/nixio/axTLS/ssl/test/microsoft.x509_ca.pem new file mode 100644 index 000000000..478e60b07 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/microsoft.x509_ca.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIPAMEAizw8iBHRPvZj7N9AMA0GCSqGSIb3DQEBBAUAMHAx +KzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAc +BgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0 +IFJvb3QgQXV0aG9yaXR5MB4XDTk3MDExMDA3MDAwMFoXDTIwMTIzMTA3MDAwMFow +cDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBDb3JwLjEe +MBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3Nv +ZnQgUm9vdCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCpAr3BcOY78k4bKJ+XeF4w6qKpjSVf+P6VTKO3/p2iID58UaKboo9gMmvRQmR5 +7qx2yVTa8uuchhyPn4Rms8VremIj1h083g8BkuiWxL8tZpqaaCaZ0Dosvwy1WCbB +RucKPjiWLKkoOajsSYNC44QPu5psVWGsgnyhYC13TOmZtGQ7mlAcMQgkFJ+p55Er +GOY9mGMUYFgFZZ8dN1KH96fvlALGG9O/VUWziYC/OuxUlE6u/ad6bXROrxjMlgko +IQBXkGBpN7tLEgc8Vv9b+6RmCgim0oFWV++2O14WgXcE2va+roCV/rDNf9anGnJc +PMq88AijIjCzBoXJsyB3E4XfAgMBAAGjgagwgaUwgaIGA1UdAQSBmjCBl4AQW9Bw +72lyniNRfhSyTY7/y6FyMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBN +aWNyb3NvZnQgQ29ycC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEh +MB8GA1UEAxMYTWljcm9zb2Z0IFJvb3QgQXV0aG9yaXR5gg8AwQCLPDyIEdE+9mPs +30AwDQYJKoZIhvcNAQEEBQADggEBAJXoC8CN85cYNe24ASTYdxHzXGAyn54Lyz4F +kYiPyTrmIfLwV5MstaBHyGLv/NfMOztaqTZUaf4kbT/JzKreBXzdMY09nxBwarv+ +Ek8YacD80EPjEVogT+pie6+qGcgrNyUtvmWhEoolD2Oj91Qc+SHJ1hXzUqxuQzIH +/YIX+OVnbA1R9r3xUse958Qw/CAxCYgdlSkaTdUdAqXxgOADtFv0sd3IV+5lScdS +VLa0AygS/5DW8AiPfriXxas3LOR65Kh343agANBqP8HSNorgQRKoNWobats14dQc +BOSoRQTIWjM4bk0cDWK3CqKM09VUP0bNHFWmcNsSOoeTdZ+n0qA= +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/ms_iis.cer b/libs/nixio/axTLS/ssl/test/ms_iis.cer new file mode 100755 index 000000000..250b926d6 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/ms_iis.cer @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB5jCCAVOgAwIBAgIQWPe7KyA+U7lLUohulwW2HDAJBgUrDgMCHQUAMCExHzAd +BgNVBAMTFmF4dGxzLmNlcm9jY2x1Yi5jb20uYXUwHhcNMDgwMzE3MTAyMTA2WhcN +MDkwMzE3MTAyMTA2WjAhMR8wHQYDVQQDExZheHRscy5jZXJvY2NsdWIuY29tLmF1 +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9JqHlQjrQMt3JW8yxcGhFagDa +D4QiIY8+KItTt13fIBt5g1AG4VXniaylSqKKYNPwVzqSWl7WhxMmoFU73veF8o4M +G0Zc5qbVB6ukrSV4WaTgHrIO6pWkyiaQ4L/eYfCo/2pByhl0IUKkf/TMN346/rFg +JgrElx01l6QHNQrzVQIDAQABoycwJTATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNV +HQ8EBwMFALAAAAAwCQYFKw4DAh0FAAOBgQAbH94H1fryngROJ//Oa0D3vvTO8CJ3 +8VW+3gQEwrPBOWmN6RV8OM0dE6pf8wD3s7PTCcM5+/HI1Qk53nUGrNiOmKM1s0JB +bvsO9RT+UF8mtdbo/n30M0MHMWPCC76baW3R+ANBp/V/z4l1ytpUTt+MHvz0VlUs +J4uJA3s3uh23Tg== +-----END CERTIFICATE----- diff --git a/libs/nixio/axTLS/ssl/test/perf_bigint.c b/libs/nixio/axTLS/ssl/test/perf_bigint.c new file mode 100644 index 000000000..a4ffab6a3 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/perf_bigint.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * Some performance testing of bigint. + */ + +#include +#include +#include +#include "ssl.h" + +/************************************************************************** + * BIGINT tests + * + **************************************************************************/ + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_SSL_CERT_VERIFICATION + RSA_CTX *rsa_ctx; + BI_CTX *ctx; + bigint *bi_data, *bi_res; + int diff, res = 1; + struct timeval tv_old, tv_new; + const char *plaintext; + uint8_t compare[MAX_KEY_BYTE_SIZE]; + int i, max_biggie = 10; /* really crank performance */ + int len; + uint8_t *buf; + + /** + * 512 bit key + */ + plaintext = /* 64 byte number */ + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"; + + len = get_file("../ssl/test/axTLS.key_512", &buf); + asn1_get_private_key(buf, len, &rsa_ctx); + ctx = rsa_ctx->bi_ctx; + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + bi_res = RSA_public(rsa_ctx, bi_data); + bi_data = bi_res; /* reuse again */ + + gettimeofday(&tv_old, NULL); + for (i = 0; i < max_biggie; i++) + { + bi_res = RSA_private(rsa_ctx, bi_copy(bi_data)); + if (i < max_biggie-1) + { + bi_free(ctx, bi_res); + } + } + + gettimeofday(&tv_new, NULL); + bi_free(ctx, bi_data); + + diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 + + (tv_new.tv_usec-tv_old.tv_usec)/1000; + printf("512 bit decrypt time: %dms\n", diff/max_biggie); + TTY_FLUSH(); + bi_export(ctx, bi_res, compare, 64); + RSA_free(rsa_ctx); + free(buf); + if (memcmp(plaintext, compare, 64) != 0) + goto end; + + /** + * 1024 bit key + */ + plaintext = /* 128 byte number */ + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"; + + len = get_file("../ssl/test/axTLS.key_1024", &buf); + asn1_get_private_key(buf, len, &rsa_ctx); + ctx = rsa_ctx->bi_ctx; + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + bi_res = RSA_public(rsa_ctx, bi_data); + bi_data = bi_res; /* reuse again */ + + gettimeofday(&tv_old, NULL); + for (i = 0; i < max_biggie; i++) + { + bi_res = RSA_private(rsa_ctx, bi_copy(bi_data)); + if (i < max_biggie-1) + { + bi_free(ctx, bi_res); + } + } + + gettimeofday(&tv_new, NULL); + bi_free(ctx, bi_data); + + diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 + + (tv_new.tv_usec-tv_old.tv_usec)/1000; + printf("1024 bit decrypt time: %dms\n", diff/max_biggie); + TTY_FLUSH(); + bi_export(ctx, bi_res, compare, 128); + RSA_free(rsa_ctx); + free(buf); + if (memcmp(plaintext, compare, 128) != 0) + goto end; + + /** + * 2048 bit key + */ + plaintext = /* 256 byte number */ + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"; + + len = get_file("../ssl/test/axTLS.key_2048", &buf); + asn1_get_private_key(buf, len, &rsa_ctx); + ctx = rsa_ctx->bi_ctx; + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + bi_res = RSA_public(rsa_ctx, bi_data); + bi_data = bi_res; /* reuse again */ + + gettimeofday(&tv_old, NULL); + for (i = 0; i < max_biggie; i++) + { + bi_res = RSA_private(rsa_ctx, bi_copy(bi_data)); + if (i < max_biggie-1) + { + bi_free(ctx, bi_res); + } + } + gettimeofday(&tv_new, NULL); + bi_free(ctx, bi_data); + + diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 + + (tv_new.tv_usec-tv_old.tv_usec)/1000; + printf("2048 bit decrypt time: %dms\n", diff/max_biggie); + TTY_FLUSH(); + bi_export(ctx, bi_res, compare, 256); + RSA_free(rsa_ctx); + free(buf); + if (memcmp(plaintext, compare, 256) != 0) + goto end; + + /** + * 4096 bit key + */ + plaintext = /* 512 byte number */ + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^" + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*^"; + + len = get_file("../ssl/test/axTLS.key_4096", &buf); + asn1_get_private_key(buf, len, &rsa_ctx); + ctx = rsa_ctx->bi_ctx; + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + gettimeofday(&tv_old, NULL); + bi_res = RSA_public(rsa_ctx, bi_data); + gettimeofday(&tv_new, NULL); + diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 + + (tv_new.tv_usec-tv_old.tv_usec)/1000; + printf("4096 bit encrypt time: %dms\n", diff); + TTY_FLUSH(); + bi_data = bi_res; /* reuse again */ + + gettimeofday(&tv_old, NULL); + for (i = 0; i < max_biggie; i++) + { + bi_res = RSA_private(rsa_ctx, bi_copy(bi_data)); + if (i < max_biggie-1) + { + bi_free(ctx, bi_res); + } + } + + gettimeofday(&tv_new, NULL); + bi_free(ctx, bi_data); + + diff = (tv_new.tv_sec-tv_old.tv_sec)*1000 + + (tv_new.tv_usec-tv_old.tv_usec)/1000; + printf("4096 bit decrypt time: %dms\n", diff/max_biggie); + TTY_FLUSH(); + bi_export(ctx, bi_res, compare, 512); + RSA_free(rsa_ctx); + free(buf); + if (memcmp(plaintext, compare, 512) != 0) + goto end; + + /* done */ + printf("Bigint performance testing complete\n"); + res = 0; + +end: + return res; +#else + return 0; +#endif +} diff --git a/libs/nixio/axTLS/ssl/test/socgen.cer b/libs/nixio/axTLS/ssl/test/socgen.cer new file mode 100755 index 0000000000000000000000000000000000000000..a4278705b77b573b2d248ed305c22b2d09adcacd GIT binary patch literal 980 zcmXqLV!mL|#B95OnTe5!NkD_`!T z;QZvw)RI&M_td=9qQsn3Lm2}pu`Kb zlb2eeSDc@mo|>nZoS$pZI3GE97+D#Z8+#dmE@WzKWVoD|Jf%l(uZI1`=4qA=*RJJA z?chpsFj>bp<>2Cnn~pKH_~~}+WSdv5Vp-g~=R*{W@9c}qf6Ug-cl@VZLQQ_OCeH@^h`oXZ!fnJj3XJKJxVqIWh1LDZD zm>L)x7%tFXpxdUMQBqQ1rLUh{l%ofXrp)5Zbg*Cbf$tUR59|fBpSpoj9n0I5NQx@ z5Gux*nVXoNs-K>jW}s*wZ@|vRs?EpDB*h}q|Map{+nV0K?8E09vaWbY=u}JU8K{aW zL+t=2fqWD@^vjCDVjz=Ha|v@D1BdA4&`;!%a z`ZjicJXrPWUp61x_6yT?-1OPGnCp)Bskw&pd3$HgaIL> +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "ssl.h" + +#define DEFAULT_CERT "../ssl/test/axTLS.x509_512.cer" +#define DEFAULT_KEY "../ssl/test/axTLS.key_512" +//#define DEFAULT_SVR_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES +#define DEFAULT_SVR_OPTION 0 +#define DEFAULT_CLNT_OPTION 0 +//#define DEFAULT_CLNT_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES + +static int g_port = 19001; + +/************************************************************************** + * AES tests + * + * Run through a couple of the RFC3602 tests to verify that AES is correct. + **************************************************************************/ +#define TEST1_SIZE 16 +#define TEST2_SIZE 32 + +static int AES_test(BI_CTX *bi_ctx) +{ + AES_CTX aes_key; + int res = 1; + uint8_t key[TEST1_SIZE]; + uint8_t iv[TEST1_SIZE]; + + { + /* + Case #1: Encrypting 16 bytes (1 block) using AES-CBC + Key : 0x06a9214036b8a15b512e03d534120006 + IV : 0x3dafba429d9eb430b422da802c9fac41 + Plaintext : "Single block msg" + Ciphertext: 0xe353779c1079aeb82708942dbe77181a + + */ + char *in_str = "Single block msg"; + uint8_t ct[TEST1_SIZE]; + uint8_t enc_data[TEST1_SIZE]; + uint8_t dec_data[TEST1_SIZE]; + + bigint *key_bi = bi_str_import( + bi_ctx, "06A9214036B8A15B512E03D534120006"); + bigint *iv_bi = bi_str_import( + bi_ctx, "3DAFBA429D9EB430B422DA802C9FAC41"); + bigint *ct_bi = bi_str_import( + bi_ctx, "E353779C1079AEB82708942DBE77181A"); + bi_export(bi_ctx, key_bi, key, TEST1_SIZE); + bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE); + bi_export(bi_ctx, ct_bi, ct, TEST1_SIZE); + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_cbc_encrypt(&aes_key, (const uint8_t *)in_str, + enc_data, sizeof(enc_data)); + if (memcmp(enc_data, ct, sizeof(ct))) + { + printf("Error: AES ENCRYPT #1 failed\n"); + goto end; + } + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_convert_key(&aes_key); + AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data)); + + if (memcmp(dec_data, in_str, sizeof(dec_data))) + { + printf("Error: AES DECRYPT #1 failed\n"); + goto end; + } + } + + { + /* + Case #2: Encrypting 32 bytes (2 blocks) using AES-CBC + Key : 0xc286696d887c9aa0611bbb3e2025a45a + IV : 0x562e17996d093d28ddb3ba695a2e6f58 + Plaintext : 0x000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a + 7586602d253cfff91b8266bea6d61ab1 + */ + uint8_t in_data[TEST2_SIZE]; + uint8_t ct[TEST2_SIZE]; + uint8_t enc_data[TEST2_SIZE]; + uint8_t dec_data[TEST2_SIZE]; + + bigint *in_bi = bi_str_import(bi_ctx, + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + bigint *key_bi = bi_str_import( + bi_ctx, "C286696D887C9AA0611BBB3E2025A45A"); + bigint *iv_bi = bi_str_import( + bi_ctx, "562E17996D093D28DDB3BA695A2E6F58"); + bigint *ct_bi = bi_str_import(bi_ctx, + "D296CD94C2CCCF8A3A863028B5E1DC0A7586602D253CFFF91B8266BEA6D61AB1"); + bi_export(bi_ctx, in_bi, in_data, TEST2_SIZE); + bi_export(bi_ctx, key_bi, key, TEST1_SIZE); + bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE); + bi_export(bi_ctx, ct_bi, ct, TEST2_SIZE); + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_cbc_encrypt(&aes_key, (const uint8_t *)in_data, + enc_data, sizeof(enc_data)); + + if (memcmp(enc_data, ct, sizeof(ct))) + { + printf("Error: ENCRYPT #2 failed\n"); + goto end; + } + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_convert_key(&aes_key); + AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data)); + if (memcmp(dec_data, in_data, sizeof(dec_data))) + { + printf("Error: DECRYPT #2 failed\n"); + goto end; + } + } + + res = 0; + printf("All AES tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * RC4 tests + * + * ARC4 tests vectors from OpenSSL (crypto/rc4/rc4test.c) + **************************************************************************/ +static const uint8_t keys[7][30]= +{ + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {4,0xef,0x01,0x23,0x45}, + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {4,0xef,0x01,0x23,0x45}, +}; + +static const uint8_t data_len[7]={8,8,8,20,28,10}; +static uint8_t data[7][30]= +{ + {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff}, + {0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0}, +}; + +static const uint8_t output[7][30]= +{ + {0x75,0xb7,0x87,0x80,0x99,0xe0,0xc5,0x96,0x00}, + {0x74,0x94,0xc2,0xe7,0x10,0x4b,0x08,0x79,0x00}, + {0xde,0x18,0x89,0x41,0xa3,0x37,0x5d,0x3a,0x00}, + {0xd6,0xa1,0x41,0xa7,0xec,0x3c,0x38,0xdf, + 0xbd,0x61,0x5a,0x11,0x62,0xe1,0xc7,0xba, + 0x36,0xb6,0x78,0x58,0x00}, + {0x66,0xa0,0x94,0x9f,0x8a,0xf7,0xd6,0x89, + 0x1f,0x7f,0x83,0x2b,0xa8,0x33,0xc0,0x0c, + 0x89,0x2e,0xbe,0x30,0x14,0x3c,0xe2,0x87, + 0x40,0x01,0x1e,0xcf,0x00}, + {0xd6,0xa1,0x41,0xa7,0xec,0x3c,0x38,0xdf,0xbd,0x61,0x00}, + {0}, +}; + +static int RC4_test(BI_CTX *bi_ctx) +{ + int i, res = 1; + RC4_CTX s; + + for (i = 0; i < 6; i++) + { + RC4_setup(&s, &keys[i][1], keys[i][0]); + RC4_crypt(&s, data[i], data[i], data_len[i]); + + if (memcmp(data[i], output[i], data_len[i])) + { + printf("Error: RC4 CRYPT #%d failed\n", i); + goto end; + } + } + + res = 0; + printf("All RC4 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * SHA1 tests + * + * Run through a couple of the RFC3174 tests to verify that SHA1 is correct. + **************************************************************************/ +static int SHA1_test(BI_CTX *bi_ctx) +{ + SHA1_CTX ctx; + uint8_t ct[SHA1_SIZE]; + uint8_t digest[SHA1_SIZE]; + int res = 1; + + { + const char *in_str = "abc"; + bigint *ct_bi = bi_str_import(bi_ctx, + "A9993E364706816ABA3E25717850C26C9CD0D89D"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + SHA1_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: SHA1 #1 failed\n"); + goto end; + } + } + + { + const char *in_str = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + bigint *ct_bi = bi_str_import(bi_ctx, + "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + SHA1_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: SHA1 #2 failed\n"); + goto end; + } + } + + res = 0; + printf("All SHA1 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * MD5 tests + * + * Run through a couple of the RFC1321 tests to verify that MD5 is correct. + **************************************************************************/ +static int MD5_test(BI_CTX *bi_ctx) +{ + MD5_CTX ctx; + uint8_t ct[MD5_SIZE]; + uint8_t digest[MD5_SIZE]; + int res = 1; + + { + const char *in_str = "abc"; + bigint *ct_bi = bi_str_import(bi_ctx, + "900150983CD24FB0D6963F7D28E17F72"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + + MD5_Init(&ctx); + MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + MD5_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: MD5 #1 failed\n"); + goto end; + } + } + + { + const char *in_str = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + bigint *ct_bi = bi_str_import( + bi_ctx, "D174AB98D277D9F5A5611C2C9F419D9F"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + + MD5_Init(&ctx); + MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + MD5_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: MD5 #2 failed\n"); + goto end; + } + } + res = 0; + printf("All MD5 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * HMAC tests + * + * Run through a couple of the RFC2202 tests to verify that HMAC is correct. + **************************************************************************/ +static int HMAC_test(BI_CTX *bi_ctx) +{ + uint8_t key[SHA1_SIZE]; + uint8_t ct[SHA1_SIZE]; + uint8_t dgst[SHA1_SIZE]; + int res = 1; + const char *key_str; + + const char *data_str = "Hi There"; + bigint *key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"); + bigint *ct_bi = bi_str_import(bi_ctx, "9294727A3638BB1C13F48EF8158BFC9D"); + bi_export(bi_ctx, key_bi, key, MD5_SIZE); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + hmac_md5((const uint8_t *)data_str, 8, key, MD5_SIZE, dgst); + if (memcmp(dgst, ct, MD5_SIZE)) + { + printf("HMAC MD5 #1 failed\n"); + goto end; + } + + data_str = "what do ya want for nothing?"; + key_str = "Jefe"; + ct_bi = bi_str_import(bi_ctx, "750C783E6AB0B503EAA86E310A5DB738"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + hmac_md5((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 4, dgst); + if (memcmp(dgst, ct, MD5_SIZE)) + { + printf("HMAC MD5 #2 failed\n"); + goto end; + } + + data_str = "Hi There"; + key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"); + bi_export(bi_ctx, key_bi, key, SHA1_SIZE); + ct_bi = bi_str_import(bi_ctx, "B617318655057264E28BC0B6FB378C8EF146BE00"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + hmac_sha1((const uint8_t *)data_str, 8, + (const uint8_t *)key, SHA1_SIZE, dgst); + if (memcmp(dgst, ct, SHA1_SIZE)) + { + printf("HMAC SHA1 #1 failed\n"); + goto end; + } + + data_str = "what do ya want for nothing?"; + key_str = "Jefe"; + ct_bi = bi_str_import(bi_ctx, "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + hmac_sha1((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 5, dgst); + if (memcmp(dgst, ct, SHA1_SIZE)) + { + printf("HMAC SHA1 failed\n"); + exit(1); + } + + res = 0; + printf("All HMAC tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * BIGINT tests + * + **************************************************************************/ +static int BIGINT_test(BI_CTX *ctx) +{ + int res = 1; + bigint *bi_data, *bi_exp, *bi_res; + const char *expnt, *plaintext, *mod; + uint8_t compare[MAX_KEY_BYTE_SIZE]; + + /** + * 512 bit key + */ + plaintext = /* 64 byte number */ + "01aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee"; + + mod = "C30773C8ABE09FCC279EE0E5343370DE" + "8B2FFDB6059271E3005A7CEEF0D35E0A" + "1F9915D95E63560836CC2EB2C289270D" + "BCAE8CAF6F5E907FC2759EE220071E1B"; + + expnt = "A1E556CD1738E10DF539E35101334E97" + "BE8D391C57A5C89A7AD9A2EA2ACA1B3D" + "F3140F5091CC535CBAA47CEC4159EE1F" + "B6A3661AFF1AB758426EAB158452A9B9"; + + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + bi_exp = int_to_bi(ctx, 0x10001); + bi_set_mod(ctx, bi_str_import(ctx, mod), 0); + bi_res = bi_mod_power(ctx, bi_data, bi_exp); + + bi_data = bi_res; /* resuse again - see if we get the original */ + + bi_exp = bi_str_import(ctx, expnt); + bi_res = bi_mod_power(ctx, bi_data, bi_exp); + bi_free_mod(ctx, 0); + + bi_export(ctx, bi_res, compare, 64); + if (memcmp(plaintext, compare, 64) != 0) + goto end; + + printf("All BIGINT tests passed\n"); + res = 0; + +end: + return res; +} + +/************************************************************************** + * RSA tests + * + * Use the results from openssl to verify PKCS1 etc + **************************************************************************/ +static int RSA_test(void) +{ + int res = 1; + const char *plaintext = /* 128 byte hex number */ + "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee2" + "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeee2\012"; + uint8_t enc_data[128], dec_data[128]; + RSA_CTX *rsa_ctx = NULL; + BI_CTX *bi_ctx; + bigint *plaintext_bi; + bigint *enc_data_bi, *dec_data_bi; + uint8_t enc_data2[128], dec_data2[128]; + int size; + int len; + uint8_t *buf; + + /* extract the private key elements */ + len = get_file("../ssl/test/axTLS.key_1024", &buf); + if (asn1_get_private_key(buf, len, &rsa_ctx) < 0) + { + goto end; + } + + free(buf); + bi_ctx = rsa_ctx->bi_ctx; + plaintext_bi = bi_import(bi_ctx, + (const uint8_t *)plaintext, strlen(plaintext)); + + /* basic rsa encrypt */ + enc_data_bi = RSA_public(rsa_ctx, plaintext_bi); + bi_export(bi_ctx, bi_copy(enc_data_bi), enc_data, sizeof(enc_data)); + + /* basic rsa decrypt */ + dec_data_bi = RSA_private(rsa_ctx, enc_data_bi); + bi_export(bi_ctx, dec_data_bi, dec_data, sizeof(dec_data)); + + if (memcmp(dec_data, plaintext, strlen(plaintext))) + { + printf("Error: DECRYPT #1 failed\n"); + goto end; + } + + RSA_encrypt(rsa_ctx, (const uint8_t *)"abc", 3, enc_data2, 0); + size = RSA_decrypt(rsa_ctx, enc_data2, dec_data2, 1); + if (memcmp("abc", dec_data2, 3)) + { + printf("Error: ENCRYPT/DECRYPT #2 failed\n"); + goto end; + } + + RSA_free(rsa_ctx); + res = 0; + printf("All RSA tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * Cert Testing + * + **************************************************************************/ +static int cert_tests(void) +{ + int res = -1, len; + X509_CTX *x509_ctx; + SSL_CTX *ssl_ctx; + uint8_t *buf; + + /* check a bunch of 3rd party certificates */ + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/microsoft.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #1\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/thawte.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #2\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/deutsche_telecom.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #3\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/equifax.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #4\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/gnutls.cer", &buf); + if ((res = add_cert(ssl_ctx, buf, len)) < 0) + { + printf("Cert #5\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/socgen.cer", &buf); + if ((res = add_cert(ssl_ctx, buf, len)) < 0) + { + printf("Cert #6\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/verisign.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) <0) + { + printf("Cert #7\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + if (get_file("../ssl/test/verisign.x509_my_cert", &buf) < 0 || + x509_new(buf, &len, &x509_ctx)) + { + printf("Cert #8\n"); + ssl_display_error(res); + goto bad_cert; + } + + x509_free(x509_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + if ((res = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, "../ssl/test/ms_iis.cer", NULL)) != SSL_OK) + { + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + res = 0; /* all ok */ + printf("All Certificate tests passed\n"); + +bad_cert: + if (res) + printf("Error: A certificate test failed\n"); + return res; +} + +/** + * init a server socket. + */ +static int server_socket_init(int *port) +{ + struct sockaddr_in serv_addr; + int server_fd; + char yes = 1; + + /* Create socket for incoming connections */ + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + return -1; + } + + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + +go_again: + /* Construct local address structure */ + memset(&serv_addr, 0, sizeof(serv_addr)); /* Zero out structure */ + serv_addr.sin_family = AF_INET; /* Internet address family */ + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + serv_addr.sin_port = htons(*port); /* Local port */ + + /* Bind to the local address */ + if (bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + (*port)++; + goto go_again; + } + /* Mark the socket so it will listen for incoming connections */ + if (listen(server_fd, 3000) < 0) + { + return -1; + } + + return server_fd; +} + +/** + * init a client socket. + */ +static int client_socket_init(uint16_t port) +{ + struct sockaddr_in address; + int client_fd; + + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("socket"); + SOCKET_CLOSE(client_fd); + client_fd = -1; + } + + return client_fd; +} + +/************************************************************************** + * SSL Server Testing + * + **************************************************************************/ +typedef struct +{ + /* not used as yet */ + int dummy; +} SVR_CTX; + +typedef struct +{ + const char *testname; + const char *openssl_option; +} client_t; + +static void do_client(client_t *clnt) +{ + char openssl_buf[2048]; + + /* make sure the main thread goes first */ + sleep(0); + + /* show the session ids in the reconnect test */ + if (strcmp(clnt->testname, "Session Reuse") == 0) + { + sprintf(openssl_buf, "echo \"hello client\" | openssl s_client " + "-connect localhost:%d %s 2>&1 | grep \"Session-ID:\"", + g_port, clnt->openssl_option); + } + else + { + sprintf(openssl_buf, "echo \"hello client\" | openssl s_client " +#ifdef WIN32 + "-connect localhost:%d -quiet %s", +#else + "-connect localhost:%d -quiet %s > /dev/null 2>&1", +#endif + g_port, clnt->openssl_option); + } + + system(openssl_buf); +} + +static int SSL_server_test( + const char *testname, + const char *openssl_option, + const char *device_cert, + const char *product_cert, + const char *private_key, + const char *ca_cert, + const char *password, + int axtls_option) +{ + int server_fd, ret = 0; + SSL_CTX *ssl_ctx = NULL; + struct sockaddr_in client_addr; + uint8_t *read_buf; + socklen_t clnt_len = sizeof(client_addr); + client_t client_data; +#ifndef WIN32 + pthread_t thread; +#endif + g_port++; + + client_data.testname = testname; + client_data.openssl_option = openssl_option; + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + if (private_key) + { + axtls_option |= SSL_NO_DEFAULT_KEY; + } + + if ((ssl_ctx = ssl_ctx_new(axtls_option, SSL_DEFAULT_SVR_SESS)) == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + if (private_key) + { + int obj_type = SSL_OBJ_RSA_KEY; + + if (strstr(private_key, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(ssl_ctx, obj_type, private_key, password)) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + } + + if (device_cert) /* test chaining */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, device_cert, NULL)) != SSL_OK) + goto error; + } + + if (product_cert) /* test chaining */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, product_cert, NULL)) != SSL_OK) + goto error; + } + + if (ca_cert) /* test adding certificate authorities */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CACERT, ca_cert, NULL)) != SSL_OK) + goto error; + } + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_client, (void *)&client_data); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_client, + (LPVOID)&client_data, 0, NULL); +#endif + + for (;;) + { + int client_fd, size = 0; + SSL *ssl; + + /* Wait for a client to connect */ + if ((client_fd = accept(server_fd, + (struct sockaddr *)&client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + /* we are ready to go */ + ssl = ssl_server_new(ssl_ctx, client_fd); + while ((size = ssl_read(ssl, &read_buf)) == SSL_OK); + SOCKET_CLOSE(client_fd); + + if (size < SSL_OK) /* got some alert or something nasty */ + { + ret = size; + + if (ret == SSL_ERROR_CONN_LOST) + { + ret = SSL_OK; + continue; + } + + break; /* we've got a problem */ + } + else /* looks more promising */ + { + if (strstr("hello client", (char *)read_buf) == NULL) + { + printf("SSL server test \"%s\" passed\n", testname); + TTY_FLUSH(); + ret = 0; + break; + } + } + + ssl_free(ssl); + } + + SOCKET_CLOSE(server_fd); + +error: + ssl_ctx_free(ssl_ctx); + return ret; +} + +int SSL_server_tests(void) +{ + int ret = -1; + struct stat stat_buf; + SVR_CTX svr_test_ctx; + memset(&svr_test_ctx, 0, sizeof(SVR_CTX)); + + printf("### starting server tests\n"); TTY_FLUSH(); + + /* Go through the algorithms */ + + /* + * TLS1 client hello + */ + if ((ret = SSL_server_test("TLSv1", "-cipher RC4-SHA -tls1", + NULL, NULL, NULL, NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128-SHA + */ + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES256-SHA + */ + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * RC4-SHA + */ + if ((ret = SSL_server_test("RC4-SHA", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * RC4-MD5 + */ + if ((ret = SSL_server_test("RC4-MD5", "-cipher RC4-MD5", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Session Reuse + * all the session id's should match for session resumption. + */ + if ((ret = SSL_server_test("Session Reuse", + "-cipher RC4-SHA -reconnect", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 512 bit RSA key + */ + if ((ret = SSL_server_test("512 bit key", "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.cer", NULL, + "../ssl/test/axTLS.key_512", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 1024 bit RSA key (check certificate chaining) + */ + if ((ret = SSL_server_test("1024 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_device.cer", + "../ssl/test/axTLS.x509_512.cer", + "../ssl/test/axTLS.device_key", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 2048 bit RSA key + */ + if ((ret = SSL_server_test("2048 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_2048.cer", NULL, + "../ssl/test/axTLS.key_2048", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 4096 bit RSA key + */ + if ((ret = SSL_server_test("4096 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_4096.cer", NULL, + "../ssl/test/axTLS.key_4096", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Client Verification + */ + if ((ret = SSL_server_test("Client Verification", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION))) + goto cleanup; + + /* this test should fail */ + if (stat("../ssl/test/axTLS.x509_bad_before.pem", &stat_buf) >= 0) + { + if ((ret = SSL_server_test("Error: Bad Before Cert", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_bad_before.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "Bad Before Cert"); + TTY_FLUSH(); + ret = 0; /* is ok */ + } + + /* this test should fail */ + if ((ret = SSL_server_test("Error: Bad After Cert", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "Bad After Cert"); + TTY_FLUSH(); + + /* + * No trusted cert + */ + if ((ret = SSL_server_test("Error: No trusted certificate", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_512.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + NULL, NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "No trusted certificate"); + TTY_FLUSH(); + + /* + * Self-signed (from the server) + */ + if ((ret = SSL_server_test("Error: Self-signed certificate (from server)", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_512.pem " + "-key ../ssl/test/axTLS.key_512.pem " + "-CAfile ../ssl/test/axTLS.ca_x509.pem ", + NULL, NULL, NULL, + NULL, NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", + "Self-signed certificate (from server)"); + TTY_FLUSH(); + + /* + * Self-signed (from the client) + */ + if ((ret = SSL_server_test("Self-signed certificate (from client)", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_512.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", + NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION))) + goto cleanup; + + /* + * Key in PEM format + */ + if ((ret = SSL_server_test("Key in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.cer", NULL, + "../ssl/test/axTLS.key_512.pem", NULL, + NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Cert in PEM format + */ + if ((ret = SSL_server_test("Cert in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.pem", NULL, + "../ssl/test/axTLS.key_512.pem", NULL, + NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Cert chain in PEM format + */ + if ((ret = SSL_server_test("Cert chain in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_device.pem", + NULL, "../ssl/test/axTLS.device_key.pem", + "../ssl/test/axTLS.ca_x509.pem", NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128 Encrypted key + */ + if ((ret = SSL_server_test("AES128 encrypted key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes128.pem", NULL, + "../ssl/test/axTLS.key_aes128.pem", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES256 Encrypted key + */ + if ((ret = SSL_server_test("AES256 encrypted key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes256.pem", NULL, + "../ssl/test/axTLS.key_aes256.pem", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128 Encrypted invalid key + */ + if ((ret = SSL_server_test("AES128 encrypted invalid key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes128.pem", NULL, + "../ssl/test/axTLS.key_aes128.pem", + NULL, "xyz", DEFAULT_SVR_OPTION)) != SSL_ERROR_INVALID_KEY) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "AES128 encrypted invalid key"); + TTY_FLUSH(); + + /* + * PKCS#8 key (encrypted) + */ + if ((ret = SSL_server_test("pkcs#8 encrypted", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.encrypted.p8", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * PKCS#8 key (unencrypted) + */ + if ((ret = SSL_server_test("pkcs#8 unencrypted", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.unencrypted.p8", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * PKCS#12 key/certificate + */ + if ((ret = SSL_server_test("pkcs#12 with CA", "-cipher RC4-SHA", + NULL, NULL, "../ssl/test/axTLS.withCA.p12", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + if ((ret = SSL_server_test("pkcs#12 no CA", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.withoutCA.p12", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + ret = 0; + +cleanup: + if (ret) + { + printf("Error: A server test failed\n"); + ssl_display_error(ret); + exit(1); + } + else + { + printf("All server tests passed\n"); TTY_FLUSH(); + } + + return ret; +} + +/************************************************************************** + * SSL Client Testing + * + **************************************************************************/ +typedef struct +{ + uint8_t session_id[SSL_SESSION_ID_SIZE]; +#ifndef WIN32 + pthread_t server_thread; +#endif + int start_server; + int stop_server; + int do_reneg; +} CLNT_SESSION_RESUME_CTX; + +typedef struct +{ + const char *testname; + const char *openssl_option; +} server_t; + +static void do_server(server_t *svr) +{ + char openssl_buf[2048]; +#ifndef WIN32 + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); +#endif + sprintf(openssl_buf, "openssl s_server -tls1 " + "-accept %d -quiet %s ", g_port, svr->openssl_option); + system(openssl_buf); +} + +static int SSL_client_test( + const char *test, + SSL_CTX **ssl_ctx, + const char *openssl_option, + CLNT_SESSION_RESUME_CTX *sess_resume, + uint32_t client_options, + const char *private_key, + const char *password, + const char *cert) +{ + server_t server_data; + SSL *ssl = NULL; + int client_fd = -1; + uint8_t *session_id = NULL; + int ret = 1; +#ifndef WIN32 + pthread_t thread; +#endif + + if (sess_resume == NULL || sess_resume->start_server) + { + g_port++; + server_data.openssl_option = openssl_option; + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_server, (void *)&server_data); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_server, + (LPVOID)&server_data, 0, NULL); +#endif + } + + usleep(200000); /* allow server to start */ + + if (*ssl_ctx == NULL) + { + if (private_key) + { + client_options |= SSL_NO_DEFAULT_KEY; + } + + if ((*ssl_ctx = ssl_ctx_new( + client_options, SSL_DEFAULT_CLNT_SESS)) == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto client_test_exit; + } + + if (private_key) + { + int obj_type = SSL_OBJ_RSA_KEY; + + if (strstr(private_key, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(*ssl_ctx, obj_type, private_key, password)) + { + ret = SSL_ERROR_INVALID_KEY; + goto client_test_exit; + } + } + + if (cert) + { + if ((ret = ssl_obj_load(*ssl_ctx, + SSL_OBJ_X509_CERT, cert, NULL)) != SSL_OK) + { + printf("could not add cert %s (%d)\n", cert, ret); + TTY_FLUSH(); + goto client_test_exit; + } + } + + if (ssl_obj_load(*ssl_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + { + printf("could not add cert auth\n"); TTY_FLUSH(); + goto client_test_exit; + } + } + + if (sess_resume && !sess_resume->start_server) + { + session_id = sess_resume->session_id; + } + + if ((client_fd = client_socket_init(g_port)) < 0) + { + printf("could not start socket on %d\n", g_port); TTY_FLUSH(); + goto client_test_exit; + } + + ssl = ssl_client_new(*ssl_ctx, client_fd, session_id, sizeof(session_id)); + + /* check the return status */ + if ((ret = ssl_handshake_status(ssl))) + goto client_test_exit; + + /* renegotiate client */ + if (sess_resume && sess_resume->do_reneg) + { + if (ssl_renegotiate(ssl) < 0) + goto client_test_exit; + } + + if (sess_resume) + { + memcpy(sess_resume->session_id, + ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE); + } + + if (IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER) && + (ret = ssl_verify_cert(ssl))) + { + goto client_test_exit; + } + + ssl_write(ssl, (uint8_t *)"hello world\n", 13); + if (sess_resume) + { + const uint8_t *sess_id = ssl_get_session_id(ssl); + int i; + + printf(" Session-ID: "); + for (i = 0; i < SSL_SESSION_ID_SIZE; i++) + { + printf("%02X", sess_id[i]); + } + printf("\n"); + TTY_FLUSH(); + } + + ret = 0; + +client_test_exit: + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + usleep(200000); /* allow openssl to say something */ + + if (sess_resume) + { + if (sess_resume->stop_server) + { + ssl_ctx_free(*ssl_ctx); + *ssl_ctx = NULL; +#ifndef WIN32 + pthread_cancel(sess_resume->server_thread); +#endif + } + else if (sess_resume->start_server) + { +#ifndef WIN32 + sess_resume->server_thread = thread; +#endif + } + } + else + { + ssl_ctx_free(*ssl_ctx); + *ssl_ctx = NULL; +#ifndef WIN32 + pthread_cancel(thread); +#endif + } + + if (ret == 0) + { + printf("SSL client test \"%s\" passed\n", test); + TTY_FLUSH(); + } + + return ret; +} + +int SSL_client_tests(void) +{ + int ret = -1; + SSL_CTX *ssl_ctx = NULL; + CLNT_SESSION_RESUME_CTX sess_resume; + memset(&sess_resume, 0, sizeof(CLNT_SESSION_RESUME_CTX)); + + sess_resume.start_server = 1; + printf("### starting client tests\n"); + + if ((ret = SSL_client_test("512 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_512.pem " + "-key ../ssl/test/axTLS.key_512.pem", &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + /* all the session id's should match for session resumption */ + sess_resume.start_server = 0; + if ((ret = SSL_client_test("Client session resumption #1", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + sess_resume.do_reneg = 1; + if ((ret = SSL_client_test("Client renegotiation", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + sess_resume.do_reneg = 0; + + sess_resume.stop_server = 1; + if ((ret = SSL_client_test("Client session resumption #2", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("1024 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_1024.pem " + "-key ../ssl/test/axTLS.key_1024.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("2048 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("4096 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_4096.pem " + "-key ../ssl/test/axTLS.key_4096.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("Server cert chaining", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_device.pem " + "-key ../ssl/test/axTLS.device_key.pem " + "-CAfile ../ssl/test/axTLS.x509_512.pem ", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + /* Check the server can verify the client */ + if ((ret = SSL_client_test("Client peer authentication", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem " + "-CAfile ../ssl/test/axTLS.ca_x509.pem " + "-verify 1 ", NULL, DEFAULT_CLNT_OPTION, + "../ssl/test/axTLS.key_1024", NULL, + "../ssl/test/axTLS.x509_1024.cer"))) + goto cleanup; + + /* Should get an "ERROR" from openssl (as the handshake fails as soon as + * the certificate verification fails) */ + if ((ret = SSL_client_test("Error: Expired cert (verify now)", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL)) != + SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + { + printf("*** Error: %d\n", ret); + goto cleanup; + } + + printf("SSL client test \"Expired cert (verify now)\" passed\n"); + + /* There is no "ERROR" from openssl */ + if ((ret = SSL_client_test("Error: Expired cert (verify later)", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem", NULL, + DEFAULT_CLNT_OPTION|SSL_SERVER_VERIFY_LATER, NULL, + NULL, NULL)) != SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + { + printf("*** Error: %d\n", ret); + goto cleanup; + } + + printf("SSL client test \"Expired cert (verify later)\" passed\n"); + ret = 0; + +cleanup: + if (ret) + { + ssl_display_error(ret); + printf("Error: A client test failed\n"); + exit(1); + } + else + { + printf("All client tests passed\n"); TTY_FLUSH(); + } + + return ret; +} + +/************************************************************************** + * SSL Basic Testing (test a big packet handshake) + * + **************************************************************************/ +static uint8_t basic_buf[256*1024]; + +static void do_basic(void) +{ + int client_fd; + SSL *ssl_clnt; + SSL_CTX *ssl_clnt_ctx = ssl_ctx_new( + DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS); + usleep(200000); /* allow server to start */ + + if ((client_fd = client_socket_init(g_port)) < 0) + goto error; + + if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + goto error; + + ssl_clnt = ssl_client_new(ssl_clnt_ctx, client_fd, NULL, 0); + + /* check the return status */ + if (ssl_handshake_status(ssl_clnt) < 0) + { + printf("YA YA\n"); + ssl_display_error(ssl_handshake_status(ssl_clnt)); + goto error; + } + + ssl_write(ssl_clnt, basic_buf, sizeof(basic_buf)); + ssl_free(ssl_clnt); + +error: + ssl_ctx_free(ssl_clnt_ctx); + SOCKET_CLOSE(client_fd); + + /* exit this thread */ +} + +static int SSL_basic_test(void) +{ + int server_fd, client_fd, ret = 0, size = 0, offset = 0; + SSL_CTX *ssl_svr_ctx = NULL; + struct sockaddr_in client_addr; + uint8_t *read_buf; + socklen_t clnt_len = sizeof(client_addr); + SSL *ssl_svr; +#ifndef WIN32 + pthread_t thread; +#endif + memset(basic_buf, 0xA5, sizeof(basic_buf)/2); + memset(&basic_buf[sizeof(basic_buf)/2], 0x5A, sizeof(basic_buf)/2); + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + ssl_svr_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS); + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_basic, NULL); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_basic, NULL, 0, NULL); +#endif + + /* Wait for a client to connect */ + if ((client_fd = accept(server_fd, + (struct sockaddr *) &client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + /* we are ready to go */ + ssl_svr = ssl_server_new(ssl_svr_ctx, client_fd); + + do + { + while ((size = ssl_read(ssl_svr, &read_buf)) == SSL_OK); + + if (size < SSL_OK) /* got some alert or something nasty */ + { + printf("Server "); + ssl_display_error(size); + ret = size; + break; + } + else /* looks more promising */ + { + if (memcmp(read_buf, &basic_buf[offset], size) != 0) + { + ret = SSL_NOT_OK; + break; + } + } + + offset += size; + } while (offset < sizeof(basic_buf)); + + printf(ret == SSL_OK && offset == sizeof(basic_buf) ? + "SSL basic test passed\n" : + "SSL basic test failed\n"); + TTY_FLUSH(); + + ssl_free(ssl_svr); + SOCKET_CLOSE(server_fd); + SOCKET_CLOSE(client_fd); + +error: + ssl_ctx_free(ssl_svr_ctx); + return ret; +} + +#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) +/************************************************************************** + * Multi-Threading Tests + * + **************************************************************************/ +#define NUM_THREADS 100 + +typedef struct +{ + SSL_CTX *ssl_clnt_ctx; + int port; + int thread_id; +} multi_t; + +void do_multi_clnt(multi_t *multi_data) +{ + int res = 1, client_fd, i; + SSL *ssl = NULL; + char tmp[5]; + + if ((client_fd = client_socket_init(multi_data->port)) < 0) + goto client_test_exit; + + sleep(1); + ssl = ssl_client_new(multi_data->ssl_clnt_ctx, client_fd, NULL, 0); + + if ((res = ssl_handshake_status(ssl))) + { + printf("Client "); + ssl_display_error(res); + goto client_test_exit; + } + + sprintf(tmp, "%d\n", multi_data->thread_id); + for (i = 0; i < 10; i++) + ssl_write(ssl, (uint8_t *)tmp, strlen(tmp)+1); + +client_test_exit: + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + free(multi_data); +} + +void do_multi_svr(SSL *ssl) +{ + uint8_t *read_buf; + int *res_ptr = malloc(sizeof(int)); + int res; + + for (;;) + { + res = ssl_read(ssl, &read_buf); + + /* kill the client */ + if (res != SSL_OK) + { + if (res == SSL_ERROR_CONN_LOST) + { + SOCKET_CLOSE(ssl->client_fd); + ssl_free(ssl); + break; + } + else if (res > 0) + { + /* do nothing */ + } + else /* some problem */ + { + printf("Server "); + ssl_display_error(res); + goto error; + } + } + } + + res = SSL_OK; +error: + *res_ptr = res; + pthread_exit(res_ptr); +} + +int multi_thread_test(void) +{ + int server_fd = -1; + SSL_CTX *ssl_server_ctx; + SSL_CTX *ssl_clnt_ctx; + pthread_t clnt_threads[NUM_THREADS]; + pthread_t svr_threads[NUM_THREADS]; + int i, res = 0; + struct sockaddr_in client_addr; + socklen_t clnt_len = sizeof(client_addr); + + printf("Do multi-threading test (takes a minute)\n"); + + ssl_server_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS); + ssl_clnt_ctx = ssl_ctx_new(DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS); + + if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + goto error; + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + for (i = 0; i < NUM_THREADS; i++) + { + multi_t *multi_data = (multi_t *)malloc(sizeof(multi_t)); + multi_data->ssl_clnt_ctx = ssl_clnt_ctx; + multi_data->port = g_port; + multi_data->thread_id = i+1; + pthread_create(&clnt_threads[i], NULL, + (void *(*)(void *))do_multi_clnt, (void *)multi_data); + pthread_detach(clnt_threads[i]); + } + + for (i = 0; i < NUM_THREADS; i++) + { + SSL *ssl_svr; + int client_fd = accept(server_fd, + (struct sockaddr *)&client_addr, &clnt_len); + + if (client_fd < 0) + goto error; + + ssl_svr = ssl_server_new(ssl_server_ctx, client_fd); + + pthread_create(&svr_threads[i], NULL, + (void *(*)(void *))do_multi_svr, (void *)ssl_svr); + } + + /* make sure we've run all of the threads */ + for (i = 0; i < NUM_THREADS; i++) + { + void *thread_res; + pthread_join(svr_threads[i], &thread_res); + + if (*((int *)thread_res) != 0) + res = 1; + + free(thread_res); + } + + if (res) + goto error; + + printf("Multi-thread test passed (%d)\n", NUM_THREADS); +error: + ssl_ctx_free(ssl_server_ctx); + ssl_ctx_free(ssl_clnt_ctx); + SOCKET_CLOSE(server_fd); + return res; +} +#endif /* !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) */ + +/************************************************************************** + * Header issue + * + **************************************************************************/ +static void do_header_issue(void) +{ + char axtls_buf[2048]; +#ifndef WIN32 + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); +#endif + sprintf(axtls_buf, "./axssl s_client -connect localhost:%d", g_port); + system(axtls_buf); +} + +static int header_issue(void) +{ + FILE *f = fopen("../ssl/test/header_issue.dat", "r"); + int server_fd = -1, client_fd = -1, ret = 1; + uint8_t buf[2048]; + int size = 0; + struct sockaddr_in client_addr; + socklen_t clnt_len = sizeof(client_addr); +#ifndef WIN32 + pthread_t thread; +#endif + + if (f == NULL || (server_fd = server_socket_init(&g_port)) < 0) + goto error; + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_header_issue, NULL); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_header_issue, + NULL, 0, NULL); +#endif + if ((client_fd = accept(server_fd, + (struct sockaddr *) &client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + size = fread(buf, 1, sizeof(buf), f); + SOCKET_WRITE(client_fd, buf, size); + usleep(200000); + + ret = 0; +error: + fclose(f); + SOCKET_CLOSE(client_fd); + SOCKET_CLOSE(server_fd); + TTY_FLUSH(); + system("killall axssl"); + return ret; +} + +/************************************************************************** + * main() + * + **************************************************************************/ +int main(int argc, char *argv[]) +{ + int ret = 1; + BI_CTX *bi_ctx; + int fd; + +#ifdef WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + WSAStartup(wVersionRequested, &wsaData); + fd = _open("test_result.txt", O_WRONLY|O_TEMPORARY|O_CREAT, _S_IWRITE); + dup2(fd, 2); /* write stderr to this file */ +#else + fd = open("/dev/null", O_WRONLY); /* write stderr to /dev/null */ + signal(SIGPIPE, SIG_IGN); /* ignore pipe errors */ + dup2(fd, 2); +#endif + + /* can't do testing in this mode */ +#if defined CONFIG_SSL_GENERATE_X509_CERT + printf("Error: Must compile with default key/certificates\n"); + exit(1); +#endif + + bi_ctx = bi_initialize(); + + if (AES_test(bi_ctx)) + { + printf("AES tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (RC4_test(bi_ctx)) + { + printf("RC4 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (MD5_test(bi_ctx)) + { + printf("MD5 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (SHA1_test(bi_ctx)) + { + printf("SHA1 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (HMAC_test(bi_ctx)) + { + printf("HMAC tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (BIGINT_test(bi_ctx)) + { + printf("BigInt tests failed!\n"); + goto cleanup; + } + TTY_FLUSH(); + + bi_terminate(bi_ctx); + + if (RSA_test()) + { + printf("RSA tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (cert_tests()) + { + printf("CERT tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + +#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) + if (multi_thread_test()) + goto cleanup; +#endif + + if (SSL_basic_test()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (SSL_client_tests()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (SSL_server_tests()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (header_issue()) + { + printf("Header tests failed\n"); TTY_FLUSH(); + goto cleanup; + } + + ret = 0; /* all ok */ + printf("**** ALL TESTS PASSED ****\n"); TTY_FLUSH(); +cleanup: + + if (ret) + printf("Error: Some tests failed!\n"); + + close(fd); + return ret; +} diff --git a/libs/nixio/axTLS/ssl/test/ssltest.c.bak b/libs/nixio/axTLS/ssl/test/ssltest.c.bak new file mode 100644 index 000000000..ca9637f9a --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/ssltest.c.bak @@ -0,0 +1,1940 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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 testing of the crypto and ssl stuff goes here. Keeps the individual code + * modules from being uncluttered with test code. + * + * This is test code - I make no apologies for the quality! + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "ssl.h" + +#define DEFAULT_CERT "../ssl/test/axTLS.x509_512.cer" +#define DEFAULT_KEY "../ssl/test/axTLS.key_512" +//#define DEFAULT_SVR_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES +#define DEFAULT_SVR_OPTION 0 +#define DEFAULT_CLNT_OPTION 0 +//#define DEFAULT_CLNT_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES + +static int g_port = 19001; + +/************************************************************************** + * AES tests + * + * Run through a couple of the RFC3602 tests to verify that AES is correct. + **************************************************************************/ +#define TEST1_SIZE 16 +#define TEST2_SIZE 32 + +static int AES_test(BI_CTX *bi_ctx) +{ + AES_CTX aes_key; + int res = 1; + uint8_t key[TEST1_SIZE]; + uint8_t iv[TEST1_SIZE]; + + { + /* + Case #1: Encrypting 16 bytes (1 block) using AES-CBC + Key : 0x06a9214036b8a15b512e03d534120006 + IV : 0x3dafba429d9eb430b422da802c9fac41 + Plaintext : "Single block msg" + Ciphertext: 0xe353779c1079aeb82708942dbe77181a + + */ + char *in_str = "Single block msg"; + uint8_t ct[TEST1_SIZE]; + uint8_t enc_data[TEST1_SIZE]; + uint8_t dec_data[TEST1_SIZE]; + + bigint *key_bi = bi_str_import( + bi_ctx, "06A9214036B8A15B512E03D534120006"); + bigint *iv_bi = bi_str_import( + bi_ctx, "3DAFBA429D9EB430B422DA802C9FAC41"); + bigint *ct_bi = bi_str_import( + bi_ctx, "E353779C1079AEB82708942DBE77181A"); + bi_export(bi_ctx, key_bi, key, TEST1_SIZE); + bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE); + bi_export(bi_ctx, ct_bi, ct, TEST1_SIZE); + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_cbc_encrypt(&aes_key, (const uint8_t *)in_str, + enc_data, sizeof(enc_data)); + if (memcmp(enc_data, ct, sizeof(ct))) + { + printf("Error: AES ENCRYPT #1 failed\n"); + goto end; + } + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_convert_key(&aes_key); + AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data)); + + if (memcmp(dec_data, in_str, sizeof(dec_data))) + { + printf("Error: AES DECRYPT #1 failed\n"); + goto end; + } + } + + { + /* + Case #2: Encrypting 32 bytes (2 blocks) using AES-CBC + Key : 0xc286696d887c9aa0611bbb3e2025a45a + IV : 0x562e17996d093d28ddb3ba695a2e6f58 + Plaintext : 0x000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a + 7586602d253cfff91b8266bea6d61ab1 + */ + uint8_t in_data[TEST2_SIZE]; + uint8_t ct[TEST2_SIZE]; + uint8_t enc_data[TEST2_SIZE]; + uint8_t dec_data[TEST2_SIZE]; + + bigint *in_bi = bi_str_import(bi_ctx, + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + bigint *key_bi = bi_str_import( + bi_ctx, "C286696D887C9AA0611BBB3E2025A45A"); + bigint *iv_bi = bi_str_import( + bi_ctx, "562E17996D093D28DDB3BA695A2E6F58"); + bigint *ct_bi = bi_str_import(bi_ctx, + "D296CD94C2CCCF8A3A863028B5E1DC0A7586602D253CFFF91B8266BEA6D61AB1"); + bi_export(bi_ctx, in_bi, in_data, TEST2_SIZE); + bi_export(bi_ctx, key_bi, key, TEST1_SIZE); + bi_export(bi_ctx, iv_bi, iv, TEST1_SIZE); + bi_export(bi_ctx, ct_bi, ct, TEST2_SIZE); + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_cbc_encrypt(&aes_key, (const uint8_t *)in_data, + enc_data, sizeof(enc_data)); + + if (memcmp(enc_data, ct, sizeof(ct))) + { + printf("Error: ENCRYPT #2 failed\n"); + goto end; + } + + AES_set_key(&aes_key, key, iv, AES_MODE_128); + AES_convert_key(&aes_key); + AES_cbc_decrypt(&aes_key, enc_data, dec_data, sizeof(enc_data)); + if (memcmp(dec_data, in_data, sizeof(dec_data))) + { + printf("Error: DECRYPT #2 failed\n"); + goto end; + } + } + + res = 0; + printf("All AES tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * RC4 tests + * + * ARC4 tests vectors from OpenSSL (crypto/rc4/rc4test.c) + **************************************************************************/ +static const uint8_t keys[7][30]= +{ + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {4,0xef,0x01,0x23,0x45}, + {8,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}, + {4,0xef,0x01,0x23,0x45}, +}; + +static const uint8_t data_len[7]={8,8,8,20,28,10}; +static uint8_t data[7][30]= +{ + {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff}, + {0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0, + 0x12,0x34,0x56,0x78,0xff}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, + {0}, +}; + +static const uint8_t output[7][30]= +{ + {0x75,0xb7,0x87,0x80,0x99,0xe0,0xc5,0x96,0x00}, + {0x74,0x94,0xc2,0xe7,0x10,0x4b,0x08,0x79,0x00}, + {0xde,0x18,0x89,0x41,0xa3,0x37,0x5d,0x3a,0x00}, + {0xd6,0xa1,0x41,0xa7,0xec,0x3c,0x38,0xdf, + 0xbd,0x61,0x5a,0x11,0x62,0xe1,0xc7,0xba, + 0x36,0xb6,0x78,0x58,0x00}, + {0x66,0xa0,0x94,0x9f,0x8a,0xf7,0xd6,0x89, + 0x1f,0x7f,0x83,0x2b,0xa8,0x33,0xc0,0x0c, + 0x89,0x2e,0xbe,0x30,0x14,0x3c,0xe2,0x87, + 0x40,0x01,0x1e,0xcf,0x00}, + {0xd6,0xa1,0x41,0xa7,0xec,0x3c,0x38,0xdf,0xbd,0x61,0x00}, + {0}, +}; + +static int RC4_test(BI_CTX *bi_ctx) +{ + int i, res = 1; + RC4_CTX s; + + for (i = 0; i < 6; i++) + { + RC4_setup(&s, &keys[i][1], keys[i][0]); + RC4_crypt(&s, data[i], data[i], data_len[i]); + + if (memcmp(data[i], output[i], data_len[i])) + { + printf("Error: RC4 CRYPT #%d failed\n", i); + goto end; + } + } + + res = 0; + printf("All RC4 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * SHA1 tests + * + * Run through a couple of the RFC3174 tests to verify that SHA1 is correct. + **************************************************************************/ +static int SHA1_test(BI_CTX *bi_ctx) +{ + SHA1_CTX ctx; + uint8_t ct[SHA1_SIZE]; + uint8_t digest[SHA1_SIZE]; + int res = 1; + + { + const char *in_str = "abc"; + bigint *ct_bi = bi_str_import(bi_ctx, + "A9993E364706816ABA3E25717850C26C9CD0D89D"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + SHA1_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: SHA1 #1 failed\n"); + goto end; + } + } + + { + const char *in_str = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + bigint *ct_bi = bi_str_import(bi_ctx, + "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + SHA1_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: SHA1 #2 failed\n"); + goto end; + } + } + + res = 0; + printf("All SHA1 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * MD5 tests + * + * Run through a couple of the RFC1321 tests to verify that MD5 is correct. + **************************************************************************/ +static int MD5_test(BI_CTX *bi_ctx) +{ + MD5_CTX ctx; + uint8_t ct[MD5_SIZE]; + uint8_t digest[MD5_SIZE]; + int res = 1; + + { + const char *in_str = "abc"; + bigint *ct_bi = bi_str_import(bi_ctx, + "900150983CD24FB0D6963F7D28E17F72"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + + MD5_Init(&ctx); + MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + MD5_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: MD5 #1 failed\n"); + goto end; + } + } + + { + const char *in_str = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + bigint *ct_bi = bi_str_import( + bi_ctx, "D174AB98D277D9F5A5611C2C9F419D9F"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + + MD5_Init(&ctx); + MD5_Update(&ctx, (const uint8_t *)in_str, strlen(in_str)); + MD5_Final(digest, &ctx); + + if (memcmp(digest, ct, sizeof(ct))) + { + printf("Error: MD5 #2 failed\n"); + goto end; + } + } + res = 0; + printf("All MD5 tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * HMAC tests + * + * Run through a couple of the RFC2202 tests to verify that HMAC is correct. + **************************************************************************/ +static int HMAC_test(BI_CTX *bi_ctx) +{ + uint8_t key[SHA1_SIZE]; + uint8_t ct[SHA1_SIZE]; + uint8_t dgst[SHA1_SIZE]; + int res = 1; + const char *key_str; + + const char *data_str = "Hi There"; + bigint *key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"); + bigint *ct_bi = bi_str_import(bi_ctx, "9294727A3638BB1C13F48EF8158BFC9D"); + bi_export(bi_ctx, key_bi, key, MD5_SIZE); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + hmac_md5((const uint8_t *)data_str, 8, key, MD5_SIZE, dgst); + if (memcmp(dgst, ct, MD5_SIZE)) + { + printf("HMAC MD5 #1 failed\n"); + goto end; + } + + data_str = "what do ya want for nothing?"; + key_str = "Jefe"; + ct_bi = bi_str_import(bi_ctx, "750C783E6AB0B503EAA86E310A5DB738"); + bi_export(bi_ctx, ct_bi, ct, MD5_SIZE); + hmac_md5((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 4, dgst); + if (memcmp(dgst, ct, MD5_SIZE)) + { + printf("HMAC MD5 #2 failed\n"); + goto end; + } + + data_str = "Hi There"; + key_bi = bi_str_import(bi_ctx, "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"); + bi_export(bi_ctx, key_bi, key, SHA1_SIZE); + ct_bi = bi_str_import(bi_ctx, "B617318655057264E28BC0B6FB378C8EF146BE00"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + hmac_sha1((const uint8_t *)data_str, 8, + (const uint8_t *)key, SHA1_SIZE, dgst); + if (memcmp(dgst, ct, SHA1_SIZE)) + { + printf("HMAC SHA1 #1 failed\n"); + goto end; + } + + data_str = "what do ya want for nothing?"; + key_str = "Jefe"; + ct_bi = bi_str_import(bi_ctx, "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79"); + bi_export(bi_ctx, ct_bi, ct, SHA1_SIZE); + + hmac_sha1((const uint8_t *)data_str, 28, (const uint8_t *)key_str, 5, dgst); + if (memcmp(dgst, ct, SHA1_SIZE)) + { + printf("HMAC SHA1 failed\n"); + exit(1); + } + + res = 0; + printf("All HMAC tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * BIGINT tests + * + **************************************************************************/ +static int BIGINT_test(BI_CTX *ctx) +{ + int res = 1; + bigint *bi_data, *bi_exp, *bi_res; + const char *expnt, *plaintext, *mod; + uint8_t compare[MAX_KEY_BYTE_SIZE]; + + /** + * 512 bit key + */ + plaintext = /* 64 byte number */ + "01aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee"; + + mod = "C30773C8ABE09FCC279EE0E5343370DE" + "8B2FFDB6059271E3005A7CEEF0D35E0A" + "1F9915D95E63560836CC2EB2C289270D" + "BCAE8CAF6F5E907FC2759EE220071E1B"; + + expnt = "A1E556CD1738E10DF539E35101334E97" + "BE8D391C57A5C89A7AD9A2EA2ACA1B3D" + "F3140F5091CC535CBAA47CEC4159EE1F" + "B6A3661AFF1AB758426EAB158452A9B9"; + + bi_data = bi_import(ctx, (uint8_t *)plaintext, strlen(plaintext)); + bi_exp = int_to_bi(ctx, 0x10001); + bi_set_mod(ctx, bi_str_import(ctx, mod), 0); + bi_res = bi_mod_power(ctx, bi_data, bi_exp); + + bi_data = bi_res; /* resuse again - see if we get the original */ + + bi_exp = bi_str_import(ctx, expnt); + bi_res = bi_mod_power(ctx, bi_data, bi_exp); + bi_free_mod(ctx, 0); + + bi_export(ctx, bi_res, compare, 64); + if (memcmp(plaintext, compare, 64) != 0) + goto end; + + printf("All BIGINT tests passed\n"); + res = 0; + +end: + return res; +} + +/************************************************************************** + * RSA tests + * + * Use the results from openssl to verify PKCS1 etc + **************************************************************************/ +static int RSA_test(void) +{ + int res = 1; + const char *plaintext = /* 128 byte hex number */ + "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeeee2" + "1aaaaaaaaaabbbbbbbbbbbbbbbccccccccccccccdddddddddddddeeeeeeeee2\012"; + uint8_t enc_data[128], dec_data[128]; + RSA_CTX *rsa_ctx = NULL; + BI_CTX *bi_ctx; + bigint *plaintext_bi; + bigint *enc_data_bi, *dec_data_bi; + uint8_t enc_data2[128], dec_data2[128]; + int size; + int len; + uint8_t *buf; + + /* extract the private key elements */ + len = get_file("../ssl/test/axTLS.key_1024", &buf); + if (asn1_get_private_key(buf, len, &rsa_ctx) < 0) + { + goto end; + } + + free(buf); + bi_ctx = rsa_ctx->bi_ctx; + plaintext_bi = bi_import(bi_ctx, + (const uint8_t *)plaintext, strlen(plaintext)); + + /* basic rsa encrypt */ + enc_data_bi = RSA_public(rsa_ctx, plaintext_bi); + bi_export(bi_ctx, bi_copy(enc_data_bi), enc_data, sizeof(enc_data)); + + /* basic rsa decrypt */ + dec_data_bi = RSA_private(rsa_ctx, enc_data_bi); + bi_export(bi_ctx, dec_data_bi, dec_data, sizeof(dec_data)); + + if (memcmp(dec_data, plaintext, strlen(plaintext))) + { + printf("Error: DECRYPT #1 failed\n"); + goto end; + } + + RSA_encrypt(rsa_ctx, (const uint8_t *)"abc", 3, enc_data2, 0); + size = RSA_decrypt(rsa_ctx, enc_data2, dec_data2, 1); + if (memcmp("abc", dec_data2, 3)) + { + printf("Error: ENCRYPT/DECRYPT #2 failed\n"); + goto end; + } + + RSA_free(rsa_ctx); + res = 0; + printf("All RSA tests passed\n"); + +end: + return res; +} + +/************************************************************************** + * Cert Testing + * + **************************************************************************/ +static int cert_tests(void) +{ + int res = -1, len; + X509_CTX *x509_ctx; + SSL_CTX *ssl_ctx; + uint8_t *buf; + + /* check a bunch of 3rd party certificates */ + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/microsoft.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #1\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/thawte.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #2\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/deutsche_telecom.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #3\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/equifax.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) < 0) + { + printf("Cert #4\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/gnutls.cer", &buf); + if ((res = add_cert(ssl_ctx, buf, len)) < 0) + { + printf("Cert #5\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/socgen.cer", &buf); + if ((res = add_cert(ssl_ctx, buf, len)) < 0) + { + printf("Cert #6\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + len = get_file("../ssl/test/verisign.x509_ca", &buf); + if ((res = add_cert_auth(ssl_ctx, buf, len)) <0) + { + printf("Cert #7\n"); + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + free(buf); + + if (get_file("../ssl/test/verisign.x509_my_cert", &buf) < 0 || + x509_new(buf, &len, &x509_ctx)) + { + printf("Cert #8\n"); + ssl_display_error(res); + goto bad_cert; + } + + x509_free(x509_ctx); + free(buf); + + ssl_ctx = ssl_ctx_new(0, 0); + if ((res = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, "../ssl/test/ms_iis.cer", NULL)) != SSL_OK) + { + ssl_display_error(res); + goto bad_cert; + } + + ssl_ctx_free(ssl_ctx); + res = 0; /* all ok */ + printf("All Certificate tests passed\n"); + +bad_cert: + if (res) + printf("Error: A certificate test failed\n"); + return res; +} + +/** + * init a server socket. + */ +static int server_socket_init(int *port) +{ + struct sockaddr_in serv_addr; + int server_fd; + char yes = 1; + + /* Create socket for incoming connections */ + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + return -1; + } + + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + +go_again: + /* Construct local address structure */ + memset(&serv_addr, 0, sizeof(serv_addr)); /* Zero out structure */ + serv_addr.sin_family = AF_INET; /* Internet address family */ + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + serv_addr.sin_port = htons(*port); /* Local port */ + + /* Bind to the local address */ + if (bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + (*port)++; + goto go_again; + } + /* Mark the socket so it will listen for incoming connections */ + if (listen(server_fd, 3000) < 0) + { + return -1; + } + + return server_fd; +} + +/** + * init a client socket. + */ +static int client_socket_init(uint16_t port) +{ + struct sockaddr_in address; + int client_fd; + + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)) < 0) + { + perror("socket"); + SOCKET_CLOSE(client_fd); + client_fd = -1; + } + + return client_fd; +} + +/************************************************************************** + * SSL Server Testing + * + **************************************************************************/ +typedef struct +{ + /* not used as yet */ + int dummy; +} SVR_CTX; + +typedef struct +{ + const char *testname; + const char *openssl_option; +} client_t; + +static void do_client(client_t *clnt) +{ + char openssl_buf[2048]; + + /* make sure the main thread goes first */ + sleep(0); + + /* show the session ids in the reconnect test */ + if (strcmp(clnt->testname, "Session Reuse") == 0) + { + sprintf(openssl_buf, "echo \"hello client\" | openssl s_client " + "-connect localhost:%d %s 2>&1 | grep \"Session-ID:\"", + g_port, clnt->openssl_option); + } + else + { + sprintf(openssl_buf, "echo \"hello client\" | openssl s_client " +#ifdef WIN32 + "-connect localhost:%d -quiet %s", +#else + "-connect localhost:%d -quiet %s > /dev/null 2>&1", +#endif + g_port, clnt->openssl_option); + } + + system(openssl_buf); +} + +static int SSL_server_test( + const char *testname, + const char *openssl_option, + const char *device_cert, + const char *product_cert, + const char *private_key, + const char *ca_cert, + const char *password, + int axolotls_option) +{ + int server_fd, ret = 0; + SSL_CTX *ssl_ctx = NULL; + struct sockaddr_in client_addr; + uint8_t *read_buf; + socklen_t clnt_len = sizeof(client_addr); + client_t client_data; +#ifndef WIN32 + pthread_t thread; +#endif + g_port++; + + client_data.testname = testname; + client_data.openssl_option = openssl_option; + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + if (private_key) + { + axolotls_option |= SSL_NO_DEFAULT_KEY; + } + + if ((ssl_ctx = ssl_ctx_new(axolotls_option, SSL_DEFAULT_SVR_SESS)) == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + if (private_key) + { + int obj_type = SSL_OBJ_RSA_KEY; + + if (strstr(private_key, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(ssl_ctx, obj_type, private_key, password)) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + } + + if (device_cert) /* test chaining */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, device_cert, NULL)) != SSL_OK) + goto error; + } + + if (product_cert) /* test chaining */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, product_cert, NULL)) != SSL_OK) + goto error; + } + + if (ca_cert) /* test adding certificate authorities */ + { + if ((ret = ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CACERT, ca_cert, NULL)) != SSL_OK) + goto error; + } + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_client, (void *)&client_data); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_client, + (LPVOID)&client_data, 0, NULL); +#endif + + for (;;) + { + int client_fd, size = 0; + SSL *ssl; + + /* Wait for a client to connect */ + if ((client_fd = accept(server_fd, + (struct sockaddr *)&client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + /* we are ready to go */ + ssl = ssl_server_new(ssl_ctx, client_fd); + while ((size = ssl_read(ssl, &read_buf)) == SSL_OK); + SOCKET_CLOSE(client_fd); + + if (size < SSL_OK) /* got some alert or something nasty */ + { + ret = size; + + if (ret == SSL_ERROR_CONN_LOST) + { + ret = SSL_OK; + continue; + } + + break; /* we've got a problem */ + } + else /* looks more promising */ + { + if (strstr("hello client", (char *)read_buf) == NULL) + { + printf("SSL server test \"%s\" passed\n", testname); + TTY_FLUSH(); + ret = 0; + break; + } + } + + ssl_free(ssl); + } + + SOCKET_CLOSE(server_fd); + +error: + ssl_ctx_free(ssl_ctx); + return ret; +} + +int SSL_server_tests(void) +{ + int ret = -1; + struct stat stat_buf; + SVR_CTX svr_test_ctx; + memset(&svr_test_ctx, 0, sizeof(SVR_CTX)); + + printf("### starting server tests\n"); TTY_FLUSH(); + + /* Go through the algorithms */ + + /* + * TLS1 client hello + */ + if ((ret = SSL_server_test("TLSv1", "-cipher RC4-SHA -tls1", + NULL, NULL, NULL, NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128-SHA + */ + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES256-SHA + */ + if ((ret = SSL_server_test("AES256-SHA", "-cipher AES128-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * RC4-SHA + */ + if ((ret = SSL_server_test("RC4-SHA", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * RC4-MD5 + */ + if ((ret = SSL_server_test("RC4-MD5", "-cipher RC4-MD5", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Session Reuse + * all the session id's should match for session resumption. + */ + if ((ret = SSL_server_test("Session Reuse", + "-cipher RC4-SHA -reconnect", + DEFAULT_CERT, NULL, DEFAULT_KEY, NULL, NULL, + DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 512 bit RSA key + */ + if ((ret = SSL_server_test("512 bit key", "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.cer", NULL, + "../ssl/test/axTLS.key_512", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 1024 bit RSA key (check certificate chaining) + */ + if ((ret = SSL_server_test("1024 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_device.cer", + "../ssl/test/axTLS.x509_512.cer", + "../ssl/test/axTLS.device_key", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 2048 bit RSA key + */ + if ((ret = SSL_server_test("2048 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_2048.cer", NULL, + "../ssl/test/axTLS.key_2048", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * 4096 bit RSA key + */ + if ((ret = SSL_server_test("4096 bit key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_4096.cer", NULL, + "../ssl/test/axTLS.key_4096", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Client Verification + */ + if ((ret = SSL_server_test("Client Verification", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION))) + goto cleanup; + + /* this test should fail */ + if (stat("../ssl/test/axTLS.x509_bad_before.pem", &stat_buf) >= 0) + { + if ((ret = SSL_server_test("Bad Before Cert", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_bad_before.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "Bad Before Cert"); + TTY_FLUSH(); + ret = 0; /* is ok */ + } + + /* this test should fail */ + if ((ret = SSL_server_test("Bad After Cert", + "-cipher RC4-SHA -tls1 " + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem ", + NULL, NULL, NULL, + "../ssl/test/axTLS.ca_x509.cer", NULL, + DEFAULT_SVR_OPTION|SSL_CLIENT_AUTHENTICATION)) != + SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "Bad After Cert"); + TTY_FLUSH(); + + /* + * Key in PEM format + */ + if ((ret = SSL_server_test("Key in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.cer", NULL, + "../ssl/test/axTLS.key_512.pem", NULL, + NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Cert in PEM format + */ + if ((ret = SSL_server_test("Cert in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_512.pem", NULL, + "../ssl/test/axTLS.key_512.pem", NULL, + NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * Cert chain in PEM format + */ + if ((ret = SSL_server_test("Cert chain in PEM format", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_device.pem", + NULL, "../ssl/test/axTLS.device_key.pem", + "../ssl/test/axTLS.ca_x509.pem", NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128 Encrypted key + */ + if ((ret = SSL_server_test("AES128 encrypted key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes128.pem", NULL, + "../ssl/test/axTLS.key_aes128.pem", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES256 Encrypted key + */ + if ((ret = SSL_server_test("AES256 encrypted key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes256.pem", NULL, + "../ssl/test/axTLS.key_aes256.pem", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * AES128 Encrypted invalid key + */ + if ((ret = SSL_server_test("AES128 encrypted invalid key", + "-cipher RC4-SHA", + "../ssl/test/axTLS.x509_aes128.pem", NULL, + "../ssl/test/axTLS.key_aes128.pem", + NULL, "xyz", DEFAULT_SVR_OPTION)) != SSL_ERROR_INVALID_KEY) + goto cleanup; + + printf("SSL server test \"%s\" passed\n", "AES128 encrypted invalid key"); + TTY_FLUSH(); + + /* + * PKCS#8 key (encrypted) + */ + if ((ret = SSL_server_test("pkcs#8 encrypted", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.encrypted.p8", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * PKCS#8 key (unencrypted) + */ + if ((ret = SSL_server_test("pkcs#8 unencrypted", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.unencrypted.p8", + NULL, NULL, DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * PKCS#12 key/certificate + */ + if ((ret = SSL_server_test("pkcs#12 with CA", "-cipher RC4-SHA", + NULL, NULL, "../ssl/test/axTLS.withCA.p12", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + if ((ret = SSL_server_test("pkcs#12 no CA", "-cipher RC4-SHA", + DEFAULT_CERT, NULL, "../ssl/test/axTLS.withoutCA.p12", + NULL, "abcd", DEFAULT_SVR_OPTION))) + goto cleanup; + + /* + * + */ + + ret = 0; + +cleanup: + if (ret) + { + printf("Error: A server test failed\n"); + ssl_display_error(ret); + exit(1); + } + else + { + printf("All server tests passed\n"); TTY_FLUSH(); + } + + return ret; +} + +/************************************************************************** + * SSL Client Testing + * + **************************************************************************/ +typedef struct +{ + uint8_t session_id[SSL_SESSION_ID_SIZE]; +#ifndef WIN32 + pthread_t server_thread; +#endif + int start_server; + int stop_server; + int do_reneg; +} CLNT_SESSION_RESUME_CTX; + +typedef struct +{ + const char *testname; + const char *openssl_option; +} server_t; + +static void do_server(server_t *svr) +{ + char openssl_buf[2048]; +#ifndef WIN32 + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); +#endif + sprintf(openssl_buf, "openssl s_server -tls1 " + "-accept %d -quiet %s ", g_port, svr->openssl_option); + system(openssl_buf); +} + +static int SSL_client_test( + const char *test, + SSL_CTX **ssl_ctx, + const char *openssl_option, + CLNT_SESSION_RESUME_CTX *sess_resume, + uint32_t client_options, + const char *private_key, + const char *password, + const char *cert) +{ + server_t server_data; + SSL *ssl = NULL; + int client_fd = -1; + uint8_t *session_id = NULL; + int ret = 1; +#ifndef WIN32 + pthread_t thread; +#endif + + if (sess_resume == NULL || sess_resume->start_server) + { + g_port++; + server_data.openssl_option = openssl_option; + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_server, (void *)&server_data); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_server, + (LPVOID)&server_data, 0, NULL); +#endif + } + + usleep(200000); /* allow server to start */ + + if (*ssl_ctx == NULL) + { + if (private_key) + { + client_options |= SSL_NO_DEFAULT_KEY; + } + + if ((*ssl_ctx = ssl_ctx_new( + client_options, SSL_DEFAULT_CLNT_SESS)) == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto client_test_exit; + } + + if (private_key) + { + int obj_type = SSL_OBJ_RSA_KEY; + + if (strstr(private_key, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(*ssl_ctx, obj_type, private_key, password)) + { + ret = SSL_ERROR_INVALID_KEY; + goto client_test_exit; + } + } + + if (cert) + { + if ((ret = ssl_obj_load(*ssl_ctx, + SSL_OBJ_X509_CERT, cert, NULL)) != SSL_OK) + { + printf("could not add cert %s (%d)\n", cert, ret); + TTY_FLUSH(); + goto client_test_exit; + } + } + + if (ssl_obj_load(*ssl_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + { + printf("could not add cert auth\n"); TTY_FLUSH(); + goto client_test_exit; + } + } + + if (sess_resume && !sess_resume->start_server) + { + session_id = sess_resume->session_id; + } + + if ((client_fd = client_socket_init(g_port)) < 0) + { + printf("could not start socket on %d\n", g_port); TTY_FLUSH(); + goto client_test_exit; + } + + ssl = ssl_client_new(*ssl_ctx, client_fd, session_id, sizeof(session_id)); + + /* check the return status */ + if ((ret = ssl_handshake_status(ssl))) + goto client_test_exit; + + /* renegotiate client */ + if (sess_resume && sess_resume->do_reneg) + { + if (ssl_renegotiate(ssl) < 0) + goto client_test_exit; + } + + if (sess_resume) + { + memcpy(sess_resume->session_id, + ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE); + } + + if (IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER) && + (ret = ssl_verify_cert(ssl))) + { + goto client_test_exit; + } + + ssl_write(ssl, (uint8_t *)"hello world\n", 13); + if (sess_resume) + { + const uint8_t *sess_id = ssl_get_session_id(ssl); + int i; + + printf(" Session-ID: "); + for (i = 0; i < SSL_SESSION_ID_SIZE; i++) + { + printf("%02X", sess_id[i]); + } + printf("\n"); + TTY_FLUSH(); + } + + ret = 0; + +client_test_exit: + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + usleep(200000); /* allow openssl to say something */ + + if (sess_resume) + { + if (sess_resume->stop_server) + { + ssl_ctx_free(*ssl_ctx); + *ssl_ctx = NULL; +#ifndef WIN32 + pthread_cancel(sess_resume->server_thread); +#endif + } + else if (sess_resume->start_server) + { +#ifndef WIN32 + sess_resume->server_thread = thread; +#endif + } + } + else + { + ssl_ctx_free(*ssl_ctx); + *ssl_ctx = NULL; +#ifndef WIN32 + pthread_cancel(thread); +#endif + } + + if (ret == 0) + { + printf("SSL client test \"%s\" passed\n", test); + TTY_FLUSH(); + } + + return ret; +} + +int SSL_client_tests(void) +{ + int ret = -1; + SSL_CTX *ssl_ctx = NULL; + CLNT_SESSION_RESUME_CTX sess_resume; + memset(&sess_resume, 0, sizeof(CLNT_SESSION_RESUME_CTX)); + + sess_resume.start_server = 1; + printf("### starting client tests\n"); + + if ((ret = SSL_client_test("512 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_512.pem " + "-key ../ssl/test/axTLS.key_512.pem", &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + /* all the session id's should match for session resumption */ + sess_resume.start_server = 0; + if ((ret = SSL_client_test("Client session resumption #1", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + sess_resume.do_reneg = 1; + if ((ret = SSL_client_test("Client renegotiation", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + sess_resume.do_reneg = 0; + + sess_resume.stop_server = 1; + if ((ret = SSL_client_test("Client session resumption #2", + &ssl_ctx, NULL, &sess_resume, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("1024 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_1024.pem " + "-key ../ssl/test/axTLS.key_1024.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("2048 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("4096 bit key", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_4096.pem " + "-key ../ssl/test/axTLS.key_4096.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + if ((ret = SSL_client_test("Server cert chaining", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_device.pem " + "-key ../ssl/test/axTLS.device_key.pem " + "-CAfile ../ssl/test/axTLS.x509_512.pem ", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL))) + goto cleanup; + + /* Check the server can verify the client */ + if ((ret = SSL_client_test("Client peer authentication", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_2048.pem " + "-key ../ssl/test/axTLS.key_2048.pem " + "-CAfile ../ssl/test/axTLS.ca_x509.pem " + "-verify 1 ", NULL, DEFAULT_CLNT_OPTION, + "../ssl/test/axTLS.key_1024", NULL, + "../ssl/test/axTLS.x509_1024.cer"))) + goto cleanup; + + /* Should get an "ERROR" from openssl (as the handshake fails as soon as + * the certificate verification fails) */ + if ((ret = SSL_client_test("Error: Expired cert (verify now)", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem", NULL, + DEFAULT_CLNT_OPTION, NULL, NULL, NULL)) != + SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + { + printf("*** Error: %d\n", ret); + goto cleanup; + } + + printf("SSL client test \"Expired cert (verify now)\" passed\n"); + + /* There is no "ERROR" from openssl */ + if ((ret = SSL_client_test("Error: Expired cert (verify later)", + &ssl_ctx, + "-cert ../ssl/test/axTLS.x509_bad_after.pem " + "-key ../ssl/test/axTLS.key_512.pem", NULL, + DEFAULT_CLNT_OPTION|SSL_SERVER_VERIFY_LATER, NULL, + NULL, NULL)) != SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED)) + { + printf("*** Error: %d\n", ret); + goto cleanup; + } + + printf("SSL client test \"Expired cert (verify later)\" passed\n"); + ret = 0; + +cleanup: + if (ret) + { + ssl_display_error(ret); + printf("Error: A client test failed\n"); + exit(1); + } + else + { + printf("All client tests passed\n"); TTY_FLUSH(); + } + + return ret; +} + +/************************************************************************** + * SSL Basic Testing (test a big packet handshake) + * + **************************************************************************/ +static uint8_t basic_buf[256*1024]; + +static void do_basic(void) +{ + int client_fd; + SSL *ssl_clnt; + SSL_CTX *ssl_clnt_ctx = ssl_ctx_new( + DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS); + usleep(200000); /* allow server to start */ + + if ((client_fd = client_socket_init(g_port)) < 0) + goto error; + + if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + goto error; + + ssl_clnt = ssl_client_new(ssl_clnt_ctx, client_fd, NULL, 0); + + /* check the return status */ + if (ssl_handshake_status(ssl_clnt) < 0) + { + printf("YA YA\n"); + ssl_display_error(ssl_handshake_status(ssl_clnt)); + goto error; + } + + ssl_write(ssl_clnt, basic_buf, sizeof(basic_buf)); + ssl_free(ssl_clnt); + +error: + ssl_ctx_free(ssl_clnt_ctx); + SOCKET_CLOSE(client_fd); + + /* exit this thread */ +} + +static int SSL_basic_test(void) +{ + int server_fd, client_fd, ret = 0, size = 0, offset = 0; + SSL_CTX *ssl_svr_ctx = NULL; + struct sockaddr_in client_addr; + uint8_t *read_buf; + socklen_t clnt_len = sizeof(client_addr); + SSL *ssl_svr; +#ifndef WIN32 + pthread_t thread; +#endif + memset(basic_buf, 0xA5, sizeof(basic_buf)/2); + memset(&basic_buf[sizeof(basic_buf)/2], 0x5A, sizeof(basic_buf)/2); + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + ssl_svr_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS); + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_basic, NULL); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_basic, NULL, 0, NULL); +#endif + + /* Wait for a client to connect */ + if ((client_fd = accept(server_fd, + (struct sockaddr *) &client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + /* we are ready to go */ + ssl_svr = ssl_server_new(ssl_svr_ctx, client_fd); + + do + { + while ((size = ssl_read(ssl_svr, &read_buf)) == SSL_OK); + + if (size < SSL_OK) /* got some alert or something nasty */ + { + printf("Server "); + ssl_display_error(size); + ret = size; + break; + } + else /* looks more promising */ + { + if (memcmp(read_buf, &basic_buf[offset], size) != 0) + { + ret = SSL_NOT_OK; + break; + } + } + + offset += size; + } while (offset < sizeof(basic_buf)); + + printf(ret == SSL_OK && offset == sizeof(basic_buf) ? + "SSL basic test passed\n" : + "SSL basic test failed\n"); + TTY_FLUSH(); + + ssl_free(ssl_svr); + SOCKET_CLOSE(server_fd); + SOCKET_CLOSE(client_fd); + +error: + ssl_ctx_free(ssl_svr_ctx); + return ret; +} + +#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) +/************************************************************************** + * Multi-Threading Tests + * + **************************************************************************/ +#define NUM_THREADS 100 + +typedef struct +{ + SSL_CTX *ssl_clnt_ctx; + int port; + int thread_id; +} multi_t; + +void do_multi_clnt(multi_t *multi_data) +{ + int res = 1, client_fd, i; + SSL *ssl = NULL; + char tmp[5]; + + if ((client_fd = client_socket_init(multi_data->port)) < 0) + goto client_test_exit; + + sleep(1); + ssl = ssl_client_new(multi_data->ssl_clnt_ctx, client_fd, NULL, 0); + + if ((res = ssl_handshake_status(ssl))) + { + printf("Client "); + ssl_display_error(res); + goto client_test_exit; + } + + sprintf(tmp, "%d\n", multi_data->thread_id); + for (i = 0; i < 10; i++) + ssl_write(ssl, (uint8_t *)tmp, strlen(tmp)+1); + +client_test_exit: + ssl_free(ssl); + SOCKET_CLOSE(client_fd); + free(multi_data); +} + +void do_multi_svr(SSL *ssl) +{ + uint8_t *read_buf; + int *res_ptr = malloc(sizeof(int)); + int res; + + for (;;) + { + res = ssl_read(ssl, &read_buf); + + /* kill the client */ + if (res != SSL_OK) + { + if (res == SSL_ERROR_CONN_LOST) + { + SOCKET_CLOSE(ssl->client_fd); + ssl_free(ssl); + break; + } + else if (res > 0) + { + /* do nothing */ + } + else /* some problem */ + { + printf("Server "); + ssl_display_error(res); + goto error; + } + } + } + + res = SSL_OK; +error: + *res_ptr = res; + pthread_exit(res_ptr); +} + +int multi_thread_test(void) +{ + int server_fd = -1; + SSL_CTX *ssl_server_ctx; + SSL_CTX *ssl_clnt_ctx; + pthread_t clnt_threads[NUM_THREADS]; + pthread_t svr_threads[NUM_THREADS]; + int i, res = 0; + struct sockaddr_in client_addr; + socklen_t clnt_len = sizeof(client_addr); + + printf("Do multi-threading test (takes a minute)\n"); + + ssl_server_ctx = ssl_ctx_new(DEFAULT_SVR_OPTION, SSL_DEFAULT_SVR_SESS); + ssl_clnt_ctx = ssl_ctx_new(DEFAULT_CLNT_OPTION, SSL_DEFAULT_CLNT_SESS); + + if (ssl_obj_load(ssl_clnt_ctx, SSL_OBJ_X509_CACERT, + "../ssl/test/axTLS.ca_x509.cer", NULL)) + goto error; + + if ((server_fd = server_socket_init(&g_port)) < 0) + goto error; + + for (i = 0; i < NUM_THREADS; i++) + { + multi_t *multi_data = (multi_t *)malloc(sizeof(multi_t)); + multi_data->ssl_clnt_ctx = ssl_clnt_ctx; + multi_data->port = g_port; + multi_data->thread_id = i+1; + pthread_create(&clnt_threads[i], NULL, + (void *(*)(void *))do_multi_clnt, (void *)multi_data); + pthread_detach(clnt_threads[i]); + } + + for (i = 0; i < NUM_THREADS; i++) + { + SSL *ssl_svr; + int client_fd = accept(server_fd, + (struct sockaddr *)&client_addr, &clnt_len); + + if (client_fd < 0) + goto error; + + ssl_svr = ssl_server_new(ssl_server_ctx, client_fd); + + pthread_create(&svr_threads[i], NULL, + (void *(*)(void *))do_multi_svr, (void *)ssl_svr); + } + + /* make sure we've run all of the threads */ + for (i = 0; i < NUM_THREADS; i++) + { + void *thread_res; + pthread_join(svr_threads[i], &thread_res); + + if (*((int *)thread_res) != 0) + res = 1; + + free(thread_res); + } + + if (res) + goto error; + + printf("Multi-thread test passed (%d)\n", NUM_THREADS); +error: + ssl_ctx_free(ssl_server_ctx); + ssl_ctx_free(ssl_clnt_ctx); + SOCKET_CLOSE(server_fd); + return res; +} +#endif /* !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) */ + +/************************************************************************** + * Header issue + * + **************************************************************************/ +static void do_header_issue(void) +{ + char axtls_buf[2048]; +#ifndef WIN32 + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); +#endif + sprintf(axtls_buf, "./axssl s_client -connect localhost:%d", g_port); + system(axtls_buf); +} + +static int header_issue(void) +{ + FILE *f = fopen("../ssl/test/header_issue.dat", "r"); + int server_fd = -1, client_fd = -1, ret = 1; + uint8_t buf[2048]; + int size = 0; + struct sockaddr_in client_addr; + socklen_t clnt_len = sizeof(client_addr); +#ifndef WIN32 + pthread_t thread; +#endif + + if (f == NULL || (server_fd = server_socket_init(&g_port)) < 0) + goto error; + +#ifndef WIN32 + pthread_create(&thread, NULL, + (void *(*)(void *))do_header_issue, NULL); + pthread_detach(thread); +#else + CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)do_header_issue, + NULL, 0, NULL); +#endif + if ((client_fd = accept(server_fd, + (struct sockaddr *) &client_addr, &clnt_len)) < 0) + { + ret = SSL_ERROR_SOCK_SETUP_FAILURE; + goto error; + } + + size = fread(buf, 1, sizeof(buf), f); + SOCKET_WRITE(client_fd, buf, size); + usleep(200000); + + ret = 0; +error: + fclose(f); + SOCKET_CLOSE(client_fd); + SOCKET_CLOSE(server_fd); + TTY_FLUSH(); + system("killall axssl"); + return ret; +} + +/************************************************************************** + * main() + * + **************************************************************************/ +int main(int argc, char *argv[]) +{ + int ret = 1; + BI_CTX *bi_ctx; + int fd; + +#ifdef WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + WSAStartup(wVersionRequested, &wsaData); + fd = _open("test_result.txt", O_WRONLY|O_TEMPORARY|O_CREAT, _S_IWRITE); + dup2(fd, 2); /* write stderr to this file */ +#else + fd = open("/dev/null", O_WRONLY); /* write stderr to /dev/null */ + signal(SIGPIPE, SIG_IGN); /* ignore pipe errors */ + dup2(fd, 2); +#endif + + /* can't do testing in this mode */ +#if defined CONFIG_SSL_GENERATE_X509_CERT + printf("Error: Must compile with default key/certificates\n"); + exit(1); +#endif + + bi_ctx = bi_initialize(); + + if (AES_test(bi_ctx)) + { + printf("AES tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (RC4_test(bi_ctx)) + { + printf("RC4 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (MD5_test(bi_ctx)) + { + printf("MD5 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (SHA1_test(bi_ctx)) + { + printf("SHA1 tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (HMAC_test(bi_ctx)) + { + printf("HMAC tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (BIGINT_test(bi_ctx)) + { + printf("BigInt tests failed!\n"); + goto cleanup; + } + TTY_FLUSH(); + + bi_terminate(bi_ctx); + + if (RSA_test()) + { + printf("RSA tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + + if (cert_tests()) + { + printf("CERT tests failed\n"); + goto cleanup; + } + TTY_FLUSH(); + +#if !defined(WIN32) && defined(CONFIG_SSL_CTX_MUTEXING) + if (multi_thread_test()) + goto cleanup; +#endif + + if (SSL_basic_test()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (SSL_client_tests()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (SSL_server_tests()) + goto cleanup; + + system("sh ../ssl/test/killopenssl.sh"); + + if (header_issue()) + { + printf("Header tests failed\n"); TTY_FLUSH(); + goto cleanup; + } + + ret = 0; /* all ok */ + printf("**** ALL TESTS PASSED ****\n"); TTY_FLUSH(); +cleanup: + + if (ret) + printf("Error: Some tests failed!\n"); + + close(fd); + return ret; +} diff --git a/libs/nixio/axTLS/ssl/test/test_axssl.sh b/libs/nixio/axTLS/ssl/test/test_axssl.sh new file mode 100755 index 000000000..acf11a630 --- /dev/null +++ b/libs/nixio/axTLS/ssl/test/test_axssl.sh @@ -0,0 +1,163 @@ +#!/bin/sh + +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT OWNER 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. +# + +# +# Test the various axssl bindings. To run it, got to the _install directory +# and run this script from there. +# + +if grep "CONFIG_PLATFORM_WIN32=y" "../config/.config" > /dev/null; then + JAVA_EXE="$JAVA_HOME/bin/java.exe" + PERL_BIN="/cygdrive/c/Perl/bin/perl" + KILL_AXSSL="kill %1" + KILL_CSHARP="kill %1" + KILL_PERL="kill %1" + KILL_JAVA="kill %1" + KILL_LUA="kill %1" +else + if grep "CONFIG_PLATFORM_CYGWIN=y" "../config/.config" > /dev/null; then + # no .net or java on cygwin + PERL_BIN=/usr/bin/perl + KILL_AXSSL="killall axssl" + KILL_PERL="killall /usr/bin/perl" + KILL_LUA="killall /usr/local/bin/lua" + else # Linux + JAVA_EXE=/usr/java/default/bin/java + PERL_BIN=/usr/bin/perl + KILL_AXSSL="killall axssl" + KILL_CSHARP="killall mono" + KILL_PERL="killall /usr/bin/perl" + RUN_CSHARP="mono" + KILL_JAVA="killall $JAVA_EXE" + KILL_LUA="killall /usr/local/bin/lua" + fi +fi + +BASE=.. +SERVER_ARGS="s_server -accept 15001 -verify -CAfile $BASE/ssl/test/axTLS.ca_x509.cer" +CLIENT_ARGS="s_client -reconnect -connect localhost:15001 -verify -CAfile $BASE/ssl/test/axTLS.ca_x509.cer -key $BASE/ssl/test/axTLS.key_1024 -cert $BASE/ssl/test/axTLS.x509_1024.cer" + +# check pem arguments +SERVER_PEM_ARGS="s_server -accept 15001 -pass abcd -key $BASE/ssl/test/axTLS.key_aes128.pem -cert $BASE/ssl/test/axTLS.x509_aes128.pem" +CLIENT_PEM_ARGS="s_client -connect localhost:15001 -CAfile $BASE/ssl/test/axTLS.ca_x509.pem -key $BASE/ssl/test/axTLS.key_1024.pem -cert $BASE/ssl/test/axTLS.x509_1024.pem" + +export LD_LIBRARY_PATH=.:`perl -e 'use Config; print $Config{archlib};'`/CORE + +if [ -x ./axssl ]; then +echo "############################# C SAMPLE ###########################" +./axssl $SERVER_ARGS & +echo "C Test passed" | ./axssl $CLIENT_ARGS +$KILL_AXSSL +sleep 1 + +./axssl $SERVER_PEM_ARGS & +echo "C Test passed" | ./axssl $CLIENT_PEM_ARGS +$KILL_AXSSL +sleep 1 +echo "### C tests complete" +fi + +if [ -f ./axtls.jar ]; then +echo "########################## JAVA SAMPLE ###########################" +"$JAVA_EXE" -jar ./axtls.jar $SERVER_ARGS & +echo "Java Test passed" | "$JAVA_EXE" -jar ./axtls.jar $CLIENT_ARGS +$KILL_JAVA +sleep 1 + +"$JAVA_EXE" -jar ./axtls.jar $SERVER_PEM_ARGS & +echo "Java Test passed" | "$JAVA_EXE" -jar ./axtls.jar $CLIENT_PEM_ARGS +$KILL_JAVA +sleep 1 + +echo "### Java tests complete" +fi + +if [ -x ./axssl.csharp.exe ]; then +echo "############################ C# SAMPLE ###########################" +$RUN_CSHARP ./axssl.csharp.exe $SERVER_ARGS & +echo "C# Test passed" | $RUN_CSHARP ./axssl.csharp.exe $CLIENT_ARGS +$KILL_CSHARP +sleep 1 + +$RUN_CSHARP ./axssl.csharp.exe $SERVER_PEM_ARGS & +echo "C# Test passed" | $RUN_CSHARP ./axssl.csharp.exe $CLIENT_PEM_ARGS +$KILL_CSHARP +sleep 1 + +echo "### C# tests complete" +fi + +if [ -x ./axssl.vbnet.exe ]; then +echo "######################## VB.NET SAMPLE ###########################" +echo $SERVER_ARGS +echo $CLIENT_ARGS +./axssl.vbnet $SERVER_ARGS & +echo "VB.NET Test passed" | ./axssl.vbnet.exe $CLIENT_ARGS +kill %1 +sleep 1 + +./axssl.vbnet $SERVER_PEM_ARGS & +echo "VB.NET Test passed" | ./axssl.vbnet.exe $CLIENT_PEM_ARGS +kill %1 +sleep 1 +echo "### VB.NET tests complete" +fi + +if [ -f ./axssl.pl ]; then +echo "########################## PERL SAMPLE ###########################" +"$PERL_BIN" ./axssl.pl $SERVER_ARGS & +echo "Perl Test passed" | "$PERL_BIN" ./axssl.pl $CLIENT_ARGS +$KILL_PERL +sleep 1 + +"$PERL_BIN" ./axssl.pl $SERVER_PEM_ARGS & +echo "Perl Test passed" | "$PERL_BIN" ./axssl.pl $CLIENT_PEM_ARGS +$KILL_PERL +sleep 1 +echo "### Perl tests complete" +fi + +if [ -f ./axssl.lua ]; then +echo "########################## LUA SAMPLE ###########################" +./axssl.lua $SERVER_ARGS & +echo "Lua Test passed" | ./axssl.lua $CLIENT_ARGS +$KILL_LUA +sleep 1 + +./axssl.lua $SERVER_PEM_ARGS & +echo "Lua Test passed" | ./axssl.lua $CLIENT_PEM_ARGS +$KILL_LUA +sleep 1 +echo "### Lua tests complete" +fi + +echo "########################## ALL TESTS COMPLETE ###########################" diff --git a/libs/nixio/axTLS/ssl/test/thawte.x509_ca b/libs/nixio/axTLS/ssl/test/thawte.x509_ca new file mode 100644 index 0000000000000000000000000000000000000000..59b1059f84086fa580dc1001303c4db4e070e572 GIT binary patch literal 811 zcmXqLVpcb3Vw$jknTe5!iILHOmyJ`a&71aK>;=YaCsSi1!zDAbtnTZF zqUYUebWG~Ec<0FydhNn{8R@BF?(Zc$Os#^xXlJqYTkF3Hd%V7_cb@q7d0SFTnS9@$ z=3Jy0cw_d3^b5OqC$WhXUzvP*x)#Hn7(bppzeI!nd~(UVCFqsMF<)Qz@y}IOZN=N% zf+J3JvhHmb+)-@5ioN%XlkanfxiaQV%!~|-i-ipY4fugEEGx{&_@9N=wue#B(wSB};{CmAjN`|kTB z=H&c*ca+=jgU1BrKYDG@a65VUh*RyJU#lbYu5J(P5PZC(RWiHDX9`CKd5-@#z5p&(TMr~SNx>{S1)iXw~p883_fotvm7>@WOHLUXNt;H*b_ z|IQe^xO{Hv`^QNljGg@tW>o80?|dhoUHjsH8z1xKWBTV_tO(!D#LURRi0nLIbTb3p zWwp-Y-R7SHR;@y@?^|{x`0o6ab&@xk`Ta5fEloQwoG`H15dW%Sy}DTBd%Xlc@q%Y{ zucp0BS^d~*aFB_*;n@8JsUPeY%RtAH{ z9z$*ePB!LH7B*p~&|pJx15pr%OPDV#wJ0+>?aaMVZA(iFv7pk_O@+AubVt#ESf!{1BgDg@B^`tkmQZ zLs0`^kQBEFkD-B)i9%9li9&X2rJZKq+vngJKw%B24vwDh#|p1}Oj&s-uBD8&^V`2V>h0S4MVG149FSHV$nz zVA5n~ViZ#b8D9)EA5X{|$b!sLWf3zFf$3ysgtM3oOkiwAMivbNH3JnG-+-}A0_yzy zjW}s*wZ@|vRs?EpDB*h}q z|Map{+nV0K?8E09vaWbY=u}JU8K^@10Q5pmetJHN9r|U(U@?$MsF{)(m>{%MIgMNe zF3gcp;g*^HXwj|JH%sakW^XY%?K}Ip+slNyvt8M1?Uz`5cz$g&-`l2-@0b62`(fXd z*3a*ARU9g<^p4fEd +#include +#include +#include +#include "ssl.h" + +/* The session expiry time */ +#define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600) + +static const uint8_t g_hello_request[] = { HS_HELLO_REQUEST, 0, 0, 0 }; +static const uint8_t g_chg_cipher_spec_pkt[] = { 1 }; +static const char * server_finished = "server finished"; +static const char * client_finished = "client finished"; + +static int do_handshake(SSL *ssl, uint8_t *buf, int read_len); +static void set_key_block(SSL *ssl, int is_write); +static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len); +static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt); +static int send_raw_packet(SSL *ssl, uint8_t protocol); + +/** + * The server will pick the cipher based on the order that the order that the + * ciphers are listed. This order is defined at compile time. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +{ SSL_RC4_128_SHA }; +#else +static void session_free(SSL_SESSION *ssl_sessions[], int sess_index); + +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +#ifdef CONFIG_SSL_PROT_LOW /* low security, fast speed */ +{ SSL_RC4_128_SHA, SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_MD5 }; +#elif CONFIG_SSL_PROT_MEDIUM /* medium security, medium speed */ +{ SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#else /* CONFIG_SSL_PROT_HIGH */ /* high security, low speed */ +{ SSL_AES256_SHA, SSL_AES128_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#endif +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/** + * The cipher map containing all the essentials for each cipher. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#else +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* AES128-SHA */ + SSL_AES128_SHA, /* AES128-SHA */ + 16, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+16+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* AES256-SHA */ + SSL_AES256_SHA, /* AES256-SHA */ + 32, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+32+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, + /* + * This protocol is from SSLv2 days and is unlikely to be used - but was + * useful for testing different possible digest algorithms. + */ + { /* RC4-MD5 */ + SSL_RC4_128_MD5, /* RC4-MD5 */ + 16, /* key size */ + 0, /* iv size */ + 2*(MD5_SIZE+16), /* key block size */ + 0, /* no padding */ + MD5_SIZE, /* digest size */ + hmac_md5, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#endif + +static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen); +static const cipher_info_t *get_cipher_info(uint8_t cipher); +static void increment_read_sequence(SSL *ssl); +static void increment_write_sequence(SSL *ssl); +static void add_hmac_digest(SSL *ssl, int snd, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf); + +/* win32 VC6.0 doesn't have variadic macros */ +#if defined(WIN32) && !defined(CONFIG_SSL_FULL_MODE) +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) {} +#endif + +/** + * Establish a new client/server context. + */ +EXP_FUNC SSL_CTX *STDCALL ssl_ctx_new(uint32_t options, int num_sessions) +{ + SSL_CTX *ssl_ctx = (SSL_CTX *)calloc(1, sizeof (SSL_CTX)); + ssl_ctx->options = options; + + if (load_key_certs(ssl_ctx) < 0) + { + free(ssl_ctx); /* can't load our key/certificate pair, so die */ + return NULL; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl_ctx->num_sessions = num_sessions; +#endif + + SSL_CTX_MUTEX_INIT(ssl_ctx->mutex); + +#ifndef CONFIG_SSL_SKELETON_MODE + if (num_sessions) + { + ssl_ctx->ssl_sessions = (SSL_SESSION **) + calloc(1, num_sessions*sizeof(SSL_SESSION *)); + } +#endif + + return ssl_ctx; +} + +/* + * Remove a client/server context. + */ +EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + int i; + + if (ssl_ctx == NULL) + return; + + ssl = ssl_ctx->head; + + /* clear out all the ssl entries */ + while (ssl) + { + SSL *next = ssl->next; + ssl_free(ssl); + ssl = next; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + /* clear out all the sessions */ + for (i = 0; i < ssl_ctx->num_sessions; i++) + session_free(ssl_ctx->ssl_sessions, i); + + free(ssl_ctx->ssl_sessions); +#endif + + i = 0; + while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf) + { + free(ssl_ctx->certs[i].buf); + ssl_ctx->certs[i++].buf = NULL; + } + +#ifdef CONFIG_SSL_CERT_VERIFICATION + remove_ca_certs(ssl_ctx->ca_cert_ctx); +#endif + ssl_ctx->chain_length = 0; + SSL_CTX_MUTEX_DESTROY(ssl_ctx->mutex); + RSA_free(ssl_ctx->rsa_ctx); + RNG_terminate(); + free(ssl_ctx); +} + +/* + * Free any used resources used by this connection. + */ +EXP_FUNC void STDCALL ssl_free(SSL *ssl) +{ + SSL_CTX *ssl_ctx; + + if (ssl == NULL) /* just ignore null pointers */ + return; + + /* spec says we must notify when we are dying */ + send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY); + + ssl_ctx = ssl->ssl_ctx; + + SSL_CTX_LOCK(ssl_ctx->mutex); + + /* adjust the server SSL list */ + if (ssl->prev) + ssl->prev->next = ssl->next; + else + ssl_ctx->head = ssl->next; + + if (ssl->next) + ssl->next->prev = ssl->prev; + else + ssl_ctx->tail = ssl->prev; + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + + /* may already be free - but be sure */ + free(ssl->encrypt_ctx); + free(ssl->decrypt_ctx); + disposable_free(ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION + x509_free(ssl->x509_ctx); +#endif + + free(ssl); +} + +/* + * Read the SSL connection and send any alerts for various errors. + */ +EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data) +{ + int ret = basic_read(ssl, in_data); + + /* check for return code so we can send an alert */ + if (ret < SSL_OK) + { + if (ret != SSL_ERROR_CONN_LOST) + { + send_alert(ssl, ret); +#ifndef CONFIG_SSL_SKELETON_MODE + /* something nasty happened, so get rid of this session */ + kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl); +#endif + } + } + + return ret; +} + +/* + * Write application data to the client + */ +EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len) +{ + int n = out_len, nw, i, tot = 0; + + /* maximum size of a TLS packet is around 16kB, so fragment */ + do + { + nw = n; + + if (nw > RT_MAX_PLAIN_LENGTH) /* fragment if necessary */ + nw = RT_MAX_PLAIN_LENGTH; + + if ((i = send_packet(ssl, PT_APP_PROTOCOL_DATA, + &out_data[tot], nw)) <= 0) + { + out_len = i; /* an error */ + break; + } + + tot += i; + n -= i; + } while (n > 0); + + return out_len; +} + +/** + * Add a certificate to the certificate chain. + */ +int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_ERROR_NO_CERT_DEFINED, i = 0; + SSL_CERT *ssl_cert; + X509_CTX *cert = NULL; + int offset; + + while (ssl_ctx->certs[i].buf && i < CONFIG_SSL_MAX_CERTS) + i++; + + if (i == CONFIG_SSL_MAX_CERTS) /* too many certs */ + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: maximum number of certs added - change of " + "compile-time configuration required\n"); +#endif + goto error; + } + + if ((ret = x509_new(buf, &offset, &cert))) + goto error; + +#if defined (CONFIG_SSL_FULL_MODE) + if (ssl_ctx->options & SSL_DISPLAY_CERTS) + x509_print(cert, NULL); +#endif + + ssl_cert = &ssl_ctx->certs[i]; + ssl_cert->size = len; + ssl_cert->buf = (uint8_t *)malloc(len); + memcpy(ssl_cert->buf, buf, len); + ssl_ctx->chain_length++; + len -= offset; + ret = SSL_OK; /* ok so far */ + + /* recurse? */ + if (len > 0) + { + ret = add_cert(ssl_ctx, &buf[offset], len); + } + +error: + x509_free(cert); /* don't need anymore */ + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Add a certificate authority. + */ +int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_ERROR_NO_CERT_DEFINED; + int i = 0; + int offset; + CA_CERT_CTX *ca_cert_ctx; + + if (ssl_ctx->ca_cert_ctx == NULL) + ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)calloc(1, sizeof(CA_CERT_CTX)); + + ca_cert_ctx = ssl_ctx->ca_cert_ctx; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + i++; + + if (i > CONFIG_X509_MAX_CA_CERTS) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: maximum number of CA certs added - change of " + "compile-time configuration required\n"); +#endif + goto error; + } + + if ((ret = x509_new(buf, &offset, &ca_cert_ctx->cert[i]))) + goto error; + + len -= offset; + ret = SSL_OK; /* ok so far */ + + /* recurse? */ + if (len > 0) + ret = add_cert_auth(ssl_ctx, &buf[offset], len); + +error: + return ret; +} + +/* + * Retrieve an X.509 distinguished name component + */ +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component) +{ + if (ssl->x509_ctx == NULL) + return NULL; + + switch (component) + { + case SSL_X509_CERT_COMMON_NAME: + return ssl->x509_ctx->cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CERT_ORGANIZATION: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATIONAL_UNIT]; + + case SSL_X509_CA_CERT_COMMON_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CA_CERT_ORGANIZATION: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CA_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]; + + default: + return NULL; + } +} + +#endif + +/* + * Find an ssl object based on the client's file descriptor. + */ +EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + SSL_CTX_LOCK(ssl_ctx->mutex); + ssl = ssl_ctx->head; + + /* search through all the ssl entries */ + while (ssl) + { + if (ssl->client_fd == client_fd) + { + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; + } + + ssl = ssl->next; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return NULL; +} + +/* + * Force the client to perform its handshake again. + */ +EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl) +{ + int ret = SSL_OK; + + disposable_new(ssl); +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (IS_SET_SSL_FLAG(SSL_IS_CLIENT)) + { + ret = do_client_connect(ssl); + } + else +#endif + { + send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_request, sizeof(g_hello_request)); + SET_SSL_FLAG(SSL_NEED_RECORD); + } + + return ret; +} + +/** + * @brief Get what we need for key info. + * @param cipher [in] The cipher information we are after + * @param key_size [out] The key size for the cipher + * @param iv_size [out] The iv size for the cipher + * @return The amount of key information we need. + */ +static const cipher_info_t *get_cipher_info(uint8_t cipher) +{ + int i; + + for (i = 0; i < NUM_PROTOCOLS; i++) + { + if (cipher_info[i].cipher == cipher) + { + return &cipher_info[i]; + } + } + + return NULL; /* error */ +} + +/* + * Get a new ssl context for a new connection. + */ +SSL *ssl_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl = (SSL *)calloc(1, sizeof(SSL)); + ssl->ssl_ctx = ssl_ctx; + ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */ + ssl->client_fd = client_fd; + ssl->flag = SSL_NEED_RECORD; + ssl->bm_data = ssl->bm_all_data+BM_RECORD_OFFSET; /* space at the start */ + ssl->hs_status = SSL_NOT_OK; /* not connected */ +#ifdef CONFIG_ENABLE_VERIFICATION + ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx; +#endif + disposable_new(ssl); + + /* a bit hacky but saves a few bytes of memory */ + ssl->flag |= ssl_ctx->options; + SSL_CTX_LOCK(ssl_ctx->mutex); + + if (ssl_ctx->head == NULL) + { + ssl_ctx->head = ssl; + ssl_ctx->tail = ssl; + } + else + { + ssl->prev = ssl_ctx->tail; + ssl_ctx->tail->next = ssl; + ssl_ctx->tail = ssl; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; +} + +/* + * Add a private key to a context. + */ +int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj) +{ + int ret = SSL_OK; + + /* get the private key details */ + if (asn1_get_private_key(ssl_obj->buf, ssl_obj->len, &ssl_ctx->rsa_ctx)) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + +error: + return ret; +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void increment_read_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->read_sequence[i]) + break; + } +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void increment_write_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->write_sequence[i]) + break; + } +} + +/** + * Work out the HMAC digest in a packet. + */ +static void add_hmac_digest(SSL *ssl, int mode, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf) +{ + int hmac_len = buf_len + 8 + SSL_RECORD_SIZE; + uint8_t *t_buf = (uint8_t *)alloca(hmac_len+10); + + memcpy(t_buf, (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) ? + ssl->write_sequence : ssl->read_sequence, 8); + memcpy(&t_buf[8], hmac_header, SSL_RECORD_SIZE); + memcpy(&t_buf[8+SSL_RECORD_SIZE], buf, buf_len); + + ssl->cipher_info->hmac(t_buf, hmac_len, + (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) ? + ssl->server_mac : ssl->client_mac, + ssl->cipher_info->digest_size, hmac_buf); + +#if 0 + print_blob("record", ssl->hmac_tx, SSL_RECORD_SIZE); + print_blob("buf", buf, buf_len); + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) + { + print_blob("write seq", ssl->write_sequence, 8); + } + else + { + print_blob("read seq", ssl->read_sequence, 8); + } + + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) + { + print_blob("server mac", + ssl->server_mac, ssl->cipher_info->digest_size); + } + else + { + print_blob("client mac", + ssl->client_mac, ssl->cipher_info->digest_size); + } + print_blob("hmac", hmac_buf, SHA1_SIZE); +#endif +} + +/** + * Verify that the digest of a packet is correct. + */ +static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len) +{ + uint8_t hmac_buf[SHA1_SIZE]; + int hmac_offset; + + if (ssl->cipher_info->padding_size) + { + hmac_offset = read_len-buf[read_len-1]-ssl->cipher_info->digest_size-1; + } + else + { + hmac_offset = read_len - ssl->cipher_info->digest_size; + } + + /* sanity check the offset */ + if (hmac_offset < 0) + { + return SSL_ERROR_INVALID_HMAC; + } + + ssl->hmac_header[3] = hmac_offset >> 8; /* insert size */ + ssl->hmac_header[4] = hmac_offset & 0xff; + add_hmac_digest(ssl, mode, ssl->hmac_header, buf, hmac_offset, hmac_buf); + + if (memcmp(hmac_buf, &buf[hmac_offset], ssl->cipher_info->digest_size)) + { + return SSL_ERROR_INVALID_HMAC; + } + + return hmac_offset; +} + +/** + * Add a packet to the end of our sent and received packets, so that we may use + * it to calculate the hash at the end. + */ +void add_packet(SSL *ssl, const uint8_t *pkt, int len) +{ + MD5_Update(&ssl->dc->md5_ctx, pkt, len); + SHA1_Update(&ssl->dc->sha1_ctx, pkt, len); +} + +/** + * Work out the MD5 PRF. + */ +static void p_hash_md5(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + hmac_md5(seed, seed_len, sec, sec_len, a1); + memcpy(&a1[MD5_SIZE], seed, seed_len); + hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + + while (olen > MD5_SIZE) + { + uint8_t a2[MD5_SIZE]; + out += MD5_SIZE; + olen -= MD5_SIZE; + + /* A(N) */ + hmac_md5(a1, MD5_SIZE, sec, sec_len, a2); + memcpy(a1, a2, MD5_SIZE); + + /* work out the actual hash */ + hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the SHA1 PRF. + */ +static void p_hash_sha1(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + hmac_sha1(seed, seed_len, sec, sec_len, a1); + memcpy(&a1[SHA1_SIZE], seed, seed_len); + hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + + while (olen > SHA1_SIZE) + { + uint8_t a2[SHA1_SIZE]; + out += SHA1_SIZE; + olen -= SHA1_SIZE; + + /* A(N) */ + hmac_sha1(a1, SHA1_SIZE, sec, sec_len, a2); + memcpy(a1, a2, SHA1_SIZE); + + /* work out the actual hash */ + hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the PRF. + */ +static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen) +{ + int len, i; + const uint8_t *S1, *S2; + uint8_t xbuf[256]; /* needs to be > the amount of key data */ + uint8_t ybuf[256]; /* needs to be > the amount of key data */ + + len = sec_len/2; + S1 = sec; + S2 = &sec[len]; + len += (sec_len & 1); /* add for odd, make longer */ + + p_hash_md5(S1, len, seed, seed_len, xbuf, olen); + p_hash_sha1(S2, len, seed, seed_len, ybuf, olen); + + for (i = 0; i < olen; i++) + out[i] = xbuf[i] ^ ybuf[i]; +} + +/** + * Generate a master secret based on the client/server random data and the + * premaster secret. + */ +void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret) +{ + uint8_t buf[128]; /* needs to be > 13+32+32 in size */ + strcpy((char *)buf, "master secret"); + memcpy(&buf[13], ssl->dc->client_random, SSL_RANDOM_SIZE); + memcpy(&buf[45], ssl->dc->server_random, SSL_RANDOM_SIZE); + prf(premaster_secret, SSL_SECRET_SIZE, buf, 77, ssl->dc->master_secret, + SSL_SECRET_SIZE); +} + +/** + * Generate a 'random' blob of data used for the generation of keys. + */ +static void generate_key_block(uint8_t *client_random, uint8_t *server_random, + uint8_t *master_secret, uint8_t *key_block, int key_block_size) +{ + uint8_t buf[128]; + strcpy((char *)buf, "key expansion"); + memcpy(&buf[13], server_random, SSL_RANDOM_SIZE); + memcpy(&buf[45], client_random, SSL_RANDOM_SIZE); + prf(master_secret, SSL_SECRET_SIZE, buf, 77, key_block, key_block_size); +} + +/** + * Calculate the digest used in the finished message. This function also + * doubles up as a certificate verify function. + */ +void finished_digest(SSL *ssl, const char *label, uint8_t *digest) +{ + uint8_t mac_buf[128]; + uint8_t *q = mac_buf; + MD5_CTX md5_ctx = ssl->dc->md5_ctx; + SHA1_CTX sha1_ctx = ssl->dc->sha1_ctx; + + if (label) + { + strcpy((char *)q, label); + q += strlen(label); + } + + MD5_Final(q, &md5_ctx); + q += MD5_SIZE; + + SHA1_Final(q, &sha1_ctx); + q += SHA1_SIZE; + + if (label) + { + prf(ssl->dc->master_secret, SSL_SECRET_SIZE, mac_buf, (int)(q-mac_buf), + digest, SSL_FINISHED_HASH_SIZE); + } + else /* for use in a certificate verify */ + { + memcpy(digest, mac_buf, MD5_SIZE + SHA1_SIZE); + } + +#if 0 + printf("label: %s\n", label); + print_blob("master secret", ssl->dc->master_secret, 48); + print_blob("mac_buf", mac_buf, q-mac_buf); + print_blob("finished digest", digest, SSL_FINISHED_HASH_SIZE); +#endif +} + +/** + * Retrieve (and initialise) the context of a cipher. + */ +static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt) +{ + switch (ssl->cipher) + { +#ifndef CONFIG_SSL_SKELETON_MODE + case SSL_AES128_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_128); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + + case SSL_AES256_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_256); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + break; + + case SSL_RC4_128_MD5: +#endif + case SSL_RC4_128_SHA: + { + RC4_CTX *rc4_ctx = (RC4_CTX *)malloc(sizeof(RC4_CTX)); + RC4_setup(rc4_ctx, key, 16); + return (void *)rc4_ctx; + } + break; + } + + return NULL; /* its all gone wrong */ +} + +/** + * Send a packet over the socket. + */ +static int send_raw_packet(SSL *ssl, uint8_t protocol) +{ + uint8_t *rec_buf = ssl->bm_all_data; + int pkt_size = SSL_RECORD_SIZE+ssl->bm_index; + int sent = 0; + int ret = SSL_OK; + + rec_buf[0] = protocol; + rec_buf[1] = 0x03; /* version = 3.1 (TLS) */ + rec_buf[2] = 0x01; + rec_buf[3] = ssl->bm_index >> 8; + rec_buf[4] = ssl->bm_index & 0xff; + + DISPLAY_BYTES(ssl, "sending %d bytes", ssl->bm_all_data, + pkt_size, pkt_size); + + while (sent < pkt_size) + { + if ((ret = SOCKET_WRITE(ssl->client_fd, + &ssl->bm_all_data[sent], pkt_size)) < 0) + { + ret = SSL_ERROR_CONN_LOST; + break; + } + + sent += ret; + + /* keep going until the write buffer has some space */ + if (sent != pkt_size) + { + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(ssl->client_fd, &wfds); + + if (select(ssl->client_fd + 1, NULL, &wfds, NULL, NULL) < 0) + { + ret = SSL_ERROR_CONN_LOST; + break; + } + } + } + + SET_SSL_FLAG(SSL_NEED_RECORD); /* reset for next time */ + ssl->bm_index = 0; + + if (protocol != PT_APP_PROTOCOL_DATA) + { + /* always return SSL_OK during handshake */ + ret = SSL_OK; + } + + return ret; +} + +/** + * Send an encrypted packet with padding bytes if necessary. + */ +int send_packet(SSL *ssl, uint8_t protocol, const uint8_t *in, int length) +{ + int msg_length = length; + int ret, pad_bytes = 0; + ssl->bm_index = msg_length; + + /* if our state is bad, don't bother */ + if (ssl->hs_status == SSL_ERROR_DEAD) + return SSL_ERROR_CONN_LOST; + + if (in) /* has the buffer already been initialised? */ + { + memcpy(ssl->bm_data, in, length); + } + + if (IS_SET_SSL_FLAG(SSL_TX_ENCRYPTED)) + { + int mode = IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + SSL_CLIENT_WRITE : SSL_SERVER_WRITE; + uint8_t hmac_header[SSL_RECORD_SIZE]; + + hmac_header[0] = protocol; + hmac_header[1] = 0x03; + hmac_header[2] = 0x01; + hmac_header[3] = length >> 8; + hmac_header[4] = length & 0xff; + + if (protocol == PT_HANDSHAKE_PROTOCOL) + { + DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, ssl->bm_index); + } + } + + /* add the packet digest */ + msg_length += ssl->cipher_info->digest_size; + ssl->bm_index = msg_length; + add_hmac_digest(ssl, mode, hmac_header, ssl->bm_data, length, + &ssl->bm_data[length]); + + /* add padding? */ + if (ssl->cipher_info->padding_size) + { + int last_blk_size = msg_length%ssl->cipher_info->padding_size; + pad_bytes = ssl->cipher_info->padding_size - last_blk_size; + + /* ensure we always have at least 1 padding byte */ + if (pad_bytes == 0) + pad_bytes += ssl->cipher_info->padding_size; + + memset(&ssl->bm_data[msg_length], pad_bytes-1, pad_bytes); + msg_length += pad_bytes; + ssl->bm_index = msg_length; + } + + DISPLAY_BYTES(ssl, "unencrypted write", ssl->bm_data, msg_length); + increment_write_sequence(ssl); + + /* now encrypt the packet */ + ssl->cipher_info->encrypt(ssl->encrypt_ctx, ssl->bm_data, + ssl->bm_data, msg_length); + } + else if (protocol == PT_HANDSHAKE_PROTOCOL) + { + DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, ssl->bm_index); + } + } + + if ((ret = send_raw_packet(ssl, protocol)) <= 0) + return ret; + + return length; /* just return what we wanted to send */ +} + +/** + * Work out the cipher keys we are going to use for this session based on the + * master secret. + */ +static void set_key_block(SSL *ssl, int is_write) +{ + const cipher_info_t *ciph_info = get_cipher_info(ssl->cipher); + uint8_t *q; + uint8_t client_key[32], server_key[32]; /* big enough for AES256 */ + uint8_t client_iv[16], server_iv[16]; /* big enough for AES128/256 */ + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + /* only do once in a handshake */ + if (ssl->dc->key_block == NULL) + { + ssl->dc->key_block = (uint8_t *)malloc(ciph_info->key_block_size); + +#if 0 + print_blob("client", ssl->dc->client_random, 32); + print_blob("server", ssl->dc->server_random, 32); + print_blob("master", ssl->dc->master_secret, SSL_SECRET_SIZE); +#endif + generate_key_block(ssl->dc->client_random, ssl->dc->server_random, + ssl->dc->master_secret, ssl->dc->key_block, + ciph_info->key_block_size); +#if 0 + print_blob("keyblock", ssl->key_block, ciph_info->key_block_size); +#endif + } + + q = ssl->dc->key_block; + + if ((is_client && is_write) || (!is_client && !is_write)) + { + memcpy(ssl->client_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + + if ((!is_client && is_write) || (is_client && !is_write)) + { + memcpy(ssl->server_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + memcpy(client_key, q, ciph_info->key_size); + q += ciph_info->key_size; + memcpy(server_key, q, ciph_info->key_size); + q += ciph_info->key_size; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (ciph_info->iv_size) /* RC4 has no IV, AES does */ + { + memcpy(client_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + memcpy(server_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + } +#endif + + free(is_write ? ssl->encrypt_ctx : ssl->decrypt_ctx); + + /* now initialise the ciphers */ + if (is_client) + { + finished_digest(ssl, server_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, client_key, client_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, server_key, server_iv, 1); + } + else + { + finished_digest(ssl, client_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, server_key, server_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, client_key, client_iv, 1); + } + + ssl->cipher_info = ciph_info; +} + +/** + * Read the SSL connection. + */ +int basic_read(SSL *ssl, uint8_t **in_data) +{ + int ret = SSL_OK; + int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + uint8_t *buf = ssl->bm_data; + + read_len = SOCKET_READ(ssl->client_fd, &buf[ssl->bm_read_index], + ssl->need_bytes-ssl->got_bytes); + + /* connection has gone, so die */ + if (read_len <= 0) + { + ret = SSL_ERROR_CONN_LOST; + ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */ + goto error; + } + + DISPLAY_BYTES(ssl, "received %d bytes", + &ssl->bm_data[ssl->bm_read_index], read_len, read_len); + + ssl->got_bytes += read_len; + ssl->bm_read_index += read_len; + + /* haven't quite got what we want, so try again later */ + if (ssl->got_bytes < ssl->need_bytes) + return SSL_OK; + + read_len = ssl->got_bytes; + ssl->got_bytes = 0; + + if (IS_SET_SSL_FLAG(SSL_NEED_RECORD)) + { + /* check for sslv2 "client hello" */ + if (buf[0] & 0x80 && buf[2] == 1 && buf[3] == 0x03) + { +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE + DISPLAY_BYTES(ssl, "ssl2 record", buf, 5); + add_packet(ssl, &buf[2], 3); + ret = process_sslv23_client_hello(ssl); +#else + printf("Error: no SSLv23 handshaking allowed\n"); TTY_FLUSH(); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + goto error; /* not an error - just get out of here */ + } + + ssl->need_bytes = (buf[3] << 8) + buf[4]; + + /* do we violate the spec with the message size? */ + if (ssl->need_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET) + { + ret = SSL_ERROR_INVALID_PROT_MSG; + goto error; + } + + CLR_SSL_FLAG(SSL_NEED_RECORD); + memcpy(ssl->hmac_header, buf, 3); /* store for hmac */ + ssl->record_type = buf[0]; + goto error; /* no error, we're done */ + } + + /* for next time - just do it now in case of an error */ + SET_SSL_FLAG(SSL_NEED_RECORD); + ssl->need_bytes = SSL_RECORD_SIZE; + + /* decrypt if we need to */ + if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED)) + { + ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len); + read_len = verify_digest(ssl, + is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, buf, read_len); + + /* does the hmac work? */ + if (read_len < 0) + { + ret = read_len; + goto error; + } + + DISPLAY_BYTES(ssl, "decrypted", buf, read_len); + increment_read_sequence(ssl); + } + + /* The main part of the SSL packet */ + switch (ssl->record_type) + { + case PT_HANDSHAKE_PROTOCOL: + ssl->dc->bm_proc_index = 0; + ret = do_handshake(ssl, buf, read_len); + break; + + case PT_CHANGE_CIPHER_SPEC: + if (ssl->next_state != HS_FINISHED) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + + /* all encrypted from now on */ + SET_SSL_FLAG(SSL_RX_ENCRYPTED); + set_key_block(ssl, 0); + memset(ssl->read_sequence, 0, 8); + break; + + case PT_APP_PROTOCOL_DATA: + if (in_data) + { + *in_data = ssl->bm_data; /* point to the work buffer */ + (*in_data)[read_len] = 0; /* null terminate just in case */ + } + + ret = read_len; + break; + + case PT_ALERT_PROTOCOL: + /* return the alert # with alert bit set */ + ret = -buf[1]; + DISPLAY_ALERT(ssl, buf[1]); + break; + + default: + ret = SSL_ERROR_INVALID_PROT_MSG; + break; + } + +error: + ssl->bm_read_index = 0; /* reset to go again */ + + if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */ + *in_data = NULL; + + return ret; +} + +/** + * Do some basic checking of data and then perform the appropriate handshaking. + */ +static int do_handshake(SSL *ssl, uint8_t *buf, int read_len) +{ + int hs_len = (buf[2]<<8) + buf[3]; + uint8_t handshake_type = buf[0]; + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + /* some integrity checking on the handshake */ + PARANOIA_CHECK(read_len-SSL_HS_HDR_SIZE, hs_len); + + if (handshake_type != ssl->next_state) + { + /* handle a special case on the client */ + if (!is_client || handshake_type != HS_CERT_REQ || + ssl->next_state != HS_SERVER_HELLO_DONE) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + } + + hs_len += SSL_HS_HDR_SIZE; /* adjust for when adding packets */ + ssl->bm_index = hs_len; /* store the size and check later */ + DISPLAY_STATE(ssl, 0, handshake_type, 0); + + if (handshake_type != HS_CERT_VERIFY && handshake_type != HS_HELLO_REQUEST) + add_packet(ssl, buf, hs_len); + +#if defined(CONFIG_SSL_ENABLE_CLIENT) + ret = is_client ? + do_clnt_handshake(ssl, handshake_type, buf, hs_len) : + do_svr_handshake(ssl, handshake_type, buf, hs_len); +#else + ret = do_svr_handshake(ssl, handshake_type, buf, hs_len); +#endif + + /* just use recursion to get the rest */ + if (hs_len < read_len && ret == SSL_OK) + ret = do_handshake(ssl, &buf[hs_len], read_len-hs_len); + +error: + return ret; +} + +/** + * Sends the change cipher spec message. We have just read a finished message + * from the client. + */ +int send_change_cipher_spec(SSL *ssl) +{ + int ret = send_packet(ssl, PT_CHANGE_CIPHER_SPEC, + g_chg_cipher_spec_pkt, sizeof(g_chg_cipher_spec_pkt)); + SET_SSL_FLAG(SSL_TX_ENCRYPTED); + set_key_block(ssl, 1); + memset(ssl->write_sequence, 0, 8); + return ret; +} + +/** + * Send a "finished" message + */ +int send_finished(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + + buf[0] = HS_FINISHED; + buf[1] = 0; + buf[2] = 0; + buf[3] = SSL_FINISHED_HASH_SIZE; + + /* now add the finished digest mac (12 bytes) */ + finished_digest(ssl, + IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + client_finished : server_finished, &buf[4]); + +#ifndef CONFIG_SSL_SKELETON_MODE + /* store in the session cache */ + if (!IS_SET_SSL_FLAG(SSL_SESSION_RESUME) && ssl->ssl_ctx->num_sessions) + { + memcpy(ssl->session->master_secret, + ssl->dc->master_secret, SSL_SECRET_SIZE); + } +#endif + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + NULL, SSL_FINISHED_HASH_SIZE+4); +} + +/** + * Send an alert message. + * Return 1 if the alert was an "error". + */ +int send_alert(SSL *ssl, int error_code) +{ + int alert_num = 0; + int is_warning = 0; + uint8_t buf[2]; + + /* Don't bother we're already dead */ + if (ssl->hs_status == SSL_ERROR_DEAD) + { + return SSL_ERROR_CONN_LOST; + } + +#ifdef CONFIG_SSL_FULL_MODE + if (IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + ssl_display_error(error_code); +#endif + + switch (error_code) + { + case SSL_ALERT_CLOSE_NOTIFY: + is_warning = 1; + alert_num = SSL_ALERT_CLOSE_NOTIFY; + break; + + case SSL_ERROR_CONN_LOST: /* don't send alert just yet */ + is_warning = 1; + break; + + case SSL_ERROR_INVALID_HANDSHAKE: + case SSL_ERROR_INVALID_PROT_MSG: + alert_num = SSL_ALERT_HANDSHAKE_FAILURE; + break; + + case SSL_ERROR_INVALID_HMAC: + case SSL_ERROR_FINISHED_INVALID: + alert_num = SSL_ALERT_BAD_RECORD_MAC; + break; + + case SSL_ERROR_INVALID_VERSION: + alert_num = SSL_ALERT_INVALID_VERSION; + break; + + case SSL_ERROR_INVALID_SESSION: + case SSL_ERROR_NO_CIPHER: + case SSL_ERROR_INVALID_KEY: + alert_num = SSL_ALERT_ILLEGAL_PARAMETER; + break; + + case SSL_ERROR_BAD_CERTIFICATE: + alert_num = SSL_ALERT_BAD_CERTIFICATE; + break; + + default: + /* a catch-all for any badly verified certificates */ + alert_num = (error_code <= SSL_X509_OFFSET) ? + SSL_ALERT_BAD_CERTIFICATE : SSL_ALERT_UNEXPECTED_MESSAGE; + break; + } + + buf[0] = is_warning ? 1 : 2; + buf[1] = alert_num; + send_packet(ssl, PT_ALERT_PROTOCOL, buf, sizeof(buf)); + DISPLAY_ALERT(ssl, alert_num); + return is_warning ? 0 : 1; +} + +/** + * Process a client finished message. + */ +int process_finished(SSL *ssl, int hs_len) +{ + uint8_t *buf = ssl->bm_data; + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + int resume = IS_SET_SSL_FLAG(SSL_SESSION_RESUME); + + PARANOIA_CHECK(ssl->bm_index, SSL_FINISHED_HASH_SIZE+4); + + /* check that we all work before we continue */ + if (memcmp(ssl->dc->final_finish_mac, &buf[4], SSL_FINISHED_HASH_SIZE)) + return SSL_ERROR_FINISHED_INVALID; + + if ((!is_client && !resume) || (is_client && resume)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + ret = send_finished(ssl); + } + + /* if we ever renegotiate */ + ssl->next_state = is_client ? HS_HELLO_REQUEST : HS_CLIENT_HELLO; + ssl->hs_status = ret; /* set the final handshake status */ + +error: + return ret; +} + +/** + * Send a certificate. + */ +int send_certificate(SSL *ssl) +{ + int i = 0; + uint8_t *buf = ssl->bm_data; + int offset = 7; + int chain_length; + + buf[0] = HS_CERTIFICATE; + buf[1] = 0; + buf[4] = 0; + + while (i < ssl->ssl_ctx->chain_length) + { + SSL_CERT *cert = &ssl->ssl_ctx->certs[i]; + buf[offset++] = 0; + buf[offset++] = cert->size >> 8; /* cert 1 length */ + buf[offset++] = cert->size & 0xff; + memcpy(&buf[offset], cert->buf, cert->size); + offset += cert->size; + i++; + } + + chain_length = offset - 7; + buf[5] = chain_length >> 8; /* cert chain length */ + buf[6] = chain_length & 0xff; + chain_length += 3; + buf[2] = chain_length >> 8; /* handshake length */ + buf[3] = chain_length & 0xff; + ssl->bm_index = offset; + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/** + * Create a blob of memory that we'll get rid of once the handshake is + * complete. + */ +void disposable_new(SSL *ssl) +{ + if (ssl->dc == NULL) + { + ssl->dc = (DISPOSABLE_CTX *)calloc(1, sizeof(DISPOSABLE_CTX)); + MD5_Init(&ssl->dc->md5_ctx); + SHA1_Init(&ssl->dc->sha1_ctx); + } +} + +/** + * Remove the temporary blob of memory. + */ +void disposable_free(SSL *ssl) +{ + if (ssl->dc) + { + free(ssl->dc->key_block); + memset(ssl->dc, 0, sizeof(DISPOSABLE_CTX)); + free(ssl->dc); + ssl->dc = NULL; + } + +} + +#ifndef CONFIG_SSL_SKELETON_MODE /* no session resumption in this mode */ +/** + * Find if an existing session has the same session id. If so, use the + * master secret from this session for session resumption. + */ +SSL_SESSION *ssl_session_update(int max_sessions, SSL_SESSION *ssl_sessions[], + SSL *ssl, const uint8_t *session_id) +{ + time_t tm = time(NULL); + time_t oldest_sess_time = tm; + SSL_SESSION *oldest_sess = NULL; + int i; + + /* no sessions? Then bail */ + if (max_sessions == 0) + return NULL; + + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + if (session_id) + { + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i]) + { + /* kill off any expired sessions */ + if (tm > ssl_sessions[i]->conn_time + SSL_EXPIRY_TIME) + { + session_free(ssl_sessions, i); + continue; + } + + /* if the session id matches, it must still be less than + the expiry time */ + if (memcmp(ssl_sessions[i]->session_id, session_id, + SSL_SESSION_ID_SIZE) == 0) + { + ssl->session_index = i; + memcpy(ssl->dc->master_secret, + ssl_sessions[i]->master_secret, SSL_SECRET_SIZE); + SET_SSL_FLAG(SSL_SESSION_RESUME); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* a session was found */ + } + } + } + } + + /* If we've got here, no matching session was found - so create one */ + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i] == NULL) + { + /* perfect, this will do */ + ssl_sessions[i] = (SSL_SESSION *)calloc(1, sizeof(SSL_SESSION)); + ssl_sessions[i]->conn_time = tm; + ssl->session_index = i; + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* return the session object */ + } + else if (ssl_sessions[i]->conn_time <= oldest_sess_time) + { + /* find the oldest session */ + oldest_sess_time = ssl_sessions[i]->conn_time; + oldest_sess = ssl_sessions[i]; + ssl->session_index = i; + } + } + + /* ok, we've used up all of our sessions. So blow the oldest session away */ + oldest_sess->conn_time = tm; + memset(oldest_sess->session_id, 0, sizeof(SSL_SESSION_ID_SIZE)); + memset(oldest_sess->master_secret, 0, sizeof(SSL_SECRET_SIZE)); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return oldest_sess; +} + +/** + * Free an existing session. + */ +static void session_free(SSL_SESSION *ssl_sessions[], int sess_index) +{ + if (ssl_sessions[sess_index]) + { + free(ssl_sessions[sess_index]); + ssl_sessions[sess_index] = NULL; + } +} + +/** + * This ssl object doesn't want this session anymore. + */ +void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl) +{ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + + if (ssl->ssl_ctx->num_sessions) + { + session_free(ssl_sessions, ssl->session_index); + ssl->session = NULL; + } + + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); +} +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/* + * Get the session id for a handshake. This will be a 32 byte sequence. + */ +EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl) +{ + return ssl->session_id; +} + +/* + * Get the session id size for a handshake. + */ +EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl) +{ + return ssl->sess_id_size; +} + +/* + * Return the cipher id (in the SSL form). + */ +EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl) +{ + return ssl->cipher; +} + +/* + * Return the status of the handshake. + */ +EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl) +{ + return ssl->hs_status; +} + +/* + * Retrieve various parameters about the SSL engine. + */ +EXP_FUNC int STDCALL ssl_get_config(int offset) +{ + switch (offset) + { + /* return the appropriate build mode */ + case SSL_BUILD_MODE: +#if defined(CONFIG_SSL_FULL_MODE) + return SSL_BUILD_FULL_MODE; +#elif defined(CONFIG_SSL_ENABLE_CLIENT) + return SSL_BUILD_ENABLE_CLIENT; +#elif defined(CONFIG_ENABLE_VERIFICATION) + return SSL_BUILD_ENABLE_VERIFICATION; +#elif defined(CONFIG_SSL_SERVER_ONLY ) + return SSL_BUILD_SERVER_ONLY; +#else + return SSL_BUILD_SKELETON_MODE; +#endif + + case SSL_MAX_CERT_CFG_OFFSET: + return CONFIG_SSL_MAX_CERTS; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_MAX_CA_CERT_CFG_OFFSET: + return CONFIG_X509_MAX_CA_CERTS; +#endif +#ifdef CONFIG_SSL_HAS_PEM + case SSL_HAS_PEM: + return 1; +#endif + default: + return 0; + } +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Authenticate a received certificate. + */ +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl) +{ + int ret; + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (ret) /* modify into an SSL error type */ + { + ret = SSL_X509_ERROR(ret); + } + + return ret; +} + +/** + * Process a certificate message. + */ +int process_certificate(SSL *ssl, X509_CTX **x509_ctx) +{ + int ret = SSL_OK; + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int cert_size, offset = 5; + int total_cert_size = (buf[offset]<<8) + buf[offset+1]; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + X509_CTX **chain = x509_ctx; + offset += 2; + + PARANOIA_CHECK(total_cert_size, offset); + + while (offset < total_cert_size) + { + offset++; /* skip empty char */ + cert_size = (buf[offset]<<8) + buf[offset+1]; + offset += 2; + + if (x509_new(&buf[offset], NULL, chain)) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + + /* DISPLAY_CERT(ssl, *chain); */ + chain = &((*chain)->next); + offset += cert_size; + } + + PARANOIA_CHECK(pkt_size, offset); + + /* if we are client we can do the verify now or later */ + if (is_client && !IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER)) + { + ret = ssl_verify_cert(ssl); + } + + ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG; + ssl->dc->bm_proc_index += offset; +error: + return ret; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/** + * Debugging routine to display SSL handshaking stuff. + */ +#ifdef CONFIG_SSL_FULL_MODE +/** + * Debugging routine to display SSL states. + */ +void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok) +{ + const char *str; + + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + printf(not_ok ? "Error - invalid State:\t" : "State:\t"); + printf(is_send ? "sending " : "receiving "); + + switch (state) + { + case HS_HELLO_REQUEST: + str = "Hello Request (0)"; + break; + + case HS_CLIENT_HELLO: + str = "Client Hello (1)"; + break; + + case HS_SERVER_HELLO: + str = "Server Hello (2)"; + break; + + case HS_CERTIFICATE: + str = "Certificate (11)"; + break; + + case HS_SERVER_KEY_XCHG: + str = "Certificate Request (12)"; + break; + + case HS_CERT_REQ: + str = "Certificate Request (13)"; + break; + + case HS_SERVER_HELLO_DONE: + str = "Server Hello Done (14)"; + break; + + case HS_CERT_VERIFY: + str = "Certificate Verify (15)"; + break; + + case HS_CLIENT_KEY_XCHG: + str = "Client Key Exchange (16)"; + break; + + case HS_FINISHED: + str = "Finished (16)"; + break; + + default: + str = "Error (Unknown)"; + + break; + } + + printf("%s\n", str); + TTY_FLUSH(); +} + +/** + * Debugging routine to display X509 certificates. + */ +void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_CERTS)) + return; + + x509_print(x509_ctx, ssl->ssl_ctx->ca_cert_ctx); + TTY_FLUSH(); +} + +/** + * Debugging routine to display RSA objects + */ +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA)) + return; + + RSA_print(rsa_ctx); + TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking bytes. + */ +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) +{ + va_list(ap); + + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_BYTES)) + return; + + va_start(ap, size); + print_blob(format, data, size, va_arg(ap, char *)); + va_end(ap); + TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking errors. + */ +EXP_FUNC void STDCALL ssl_display_error(int error_code) +{ + if (error_code == SSL_OK) + return; + + printf("Error: "); + + /* X509 error? */ + if (error_code < SSL_X509_OFFSET) + { + printf("%s\n", x509_display_error(error_code - SSL_X509_OFFSET)); + return; + } + + /* SSL alert error code */ + if (error_code > SSL_ERROR_CONN_LOST) + { + printf("SSL error %d\n", -error_code); + return; + } + + switch (error_code) + { + case SSL_ERROR_DEAD: + printf("connection dead"); + break; + + case SSL_ERROR_INVALID_HANDSHAKE: + printf("invalid handshake"); + break; + + case SSL_ERROR_INVALID_PROT_MSG: + printf("invalid protocol message"); + break; + + case SSL_ERROR_INVALID_HMAC: + printf("invalid mac"); + break; + + case SSL_ERROR_INVALID_VERSION: + printf("invalid version"); + break; + + case SSL_ERROR_INVALID_SESSION: + printf("invalid session"); + break; + + case SSL_ERROR_NO_CIPHER: + printf("no cipher"); + break; + + case SSL_ERROR_CONN_LOST: + printf("connection lost"); + break; + + case SSL_ERROR_BAD_CERTIFICATE: + printf("bad certificate"); + break; + + case SSL_ERROR_INVALID_KEY: + printf("invalid key"); + break; + + case SSL_ERROR_FINISHED_INVALID: + printf("finished invalid"); + break; + + case SSL_ERROR_NO_CERT_DEFINED: + printf("no certificate defined"); + break; + + case SSL_ERROR_NOT_SUPPORTED: + printf("Option not supported"); + break; + + default: + printf("undefined as yet - %d", error_code); + break; + } + + printf("\n"); + TTY_FLUSH(); +} + +/** + * Debugging routine to display alerts. + */ +void DISPLAY_ALERT(SSL *ssl, int alert) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + printf("Alert: "); + + switch (alert) + { + case SSL_ALERT_CLOSE_NOTIFY: + printf("close notify"); + break; + + case SSL_ALERT_INVALID_VERSION: + printf("invalid version"); + break; + + case SSL_ALERT_BAD_CERTIFICATE: + printf("bad certificate"); + break; + + case SSL_ALERT_UNEXPECTED_MESSAGE: + printf("unexpected message"); + break; + + case SSL_ALERT_BAD_RECORD_MAC: + printf("bad record mac"); + break; + + case SSL_ALERT_HANDSHAKE_FAILURE: + printf("handshake failure"); + break; + + case SSL_ALERT_ILLEGAL_PARAMETER: + printf("illegal parameter"); + break; + + case SSL_ALERT_DECODE_ERROR: + printf("decode error"); + break; + + case SSL_ALERT_DECRYPT_ERROR: + printf("decrypt error"); + break; + + default: + printf("alert - (unknown %d)", alert); + break; + } + + printf("\n"); + TTY_FLUSH(); +} + +#endif /* CONFIG_SSL_FULL_MODE */ + +/** + * Return the version of this library. + */ +EXP_FUNC const char * STDCALL ssl_version() +{ + static const char * axtls_version = AXTLS_VERSION; + return axtls_version; +} + +/** + * Enable the various language bindings to work regardless of the + * configuration - they just return an error statement and a bad return code. + */ +#if !defined(CONFIG_SSL_FULL_MODE) +EXP_FUNC void STDCALL ssl_display_error(int error_code) {} +#endif + +#ifdef CONFIG_BINDINGS +#if !defined(CONFIG_SSL_ENABLE_CLIENT) +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + printf(unsupported_str); + return NULL; +} +#endif + +#if !defined(CONFIG_SSL_CERT_VERIFICATION) +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl) +{ + printf(unsupported_str); + return -1; +} + +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component) +{ + printf(unsupported_str); + return NULL; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +#endif /* CONFIG_BINDINGS */ + diff --git a/libs/nixio/axTLS/ssl/tls1.h b/libs/nixio/axTLS/ssl/tls1.h new file mode 100755 index 000000000..b64b4fda6 --- /dev/null +++ b/libs/nixio/axTLS/ssl/tls1.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @file tls1.h + * + * @brief The definitions for the TLS library. + */ +#ifndef HEADER_SSL_LIB_H +#define HEADER_SSL_LIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include "crypto.h" +#include "os_port.h" +#include "crypto_misc.h" + +#define SSL_RANDOM_SIZE 32 +#define SSL_SECRET_SIZE 48 +#define SSL_FINISHED_HASH_SIZE 12 +#define SSL_RECORD_SIZE 5 +#define SSL_SERVER_READ 0 +#define SSL_SERVER_WRITE 1 +#define SSL_CLIENT_READ 2 +#define SSL_CLIENT_WRITE 3 +#define SSL_HS_HDR_SIZE 4 + +/* the flags we use while establishing a connection */ +#define SSL_NEED_RECORD 0x0001 +#define SSL_TX_ENCRYPTED 0x0002 +#define SSL_RX_ENCRYPTED 0x0004 +#define SSL_SESSION_RESUME 0x0008 +#define SSL_IS_CLIENT 0x0010 +#define SSL_HAS_CERT_REQ 0x0020 + +/* some macros to muck around with flag bits */ +#define SET_SSL_FLAG(A) (ssl->flag |= A) +#define CLR_SSL_FLAG(A) (ssl->flag &= ~A) +#define IS_SET_SSL_FLAG(A) (ssl->flag & A) + +#define MAX_KEY_BYTE_SIZE 512 /* for a 4096 bit key */ +#define RT_MAX_PLAIN_LENGTH 16384 +#define RT_EXTRA 1024 +#define BM_RECORD_OFFSET 5 + +#ifdef CONFIG_SSL_SKELETON_MODE +#define NUM_PROTOCOLS 1 +#else +#define NUM_PROTOCOLS 4 +#endif + +#define PARANOIA_CHECK(A, B) if (A < B) { \ + ret = SSL_ERROR_INVALID_HANDSHAKE; goto error; } + +/* protocol types */ +enum +{ + PT_CHANGE_CIPHER_SPEC = 20, + PT_ALERT_PROTOCOL, + PT_HANDSHAKE_PROTOCOL, + PT_APP_PROTOCOL_DATA +}; + +/* handshaking types */ +enum +{ + HS_HELLO_REQUEST, + HS_CLIENT_HELLO, + HS_SERVER_HELLO, + HS_CERTIFICATE = 11, + HS_SERVER_KEY_XCHG, + HS_CERT_REQ, + HS_SERVER_HELLO_DONE, + HS_CERT_VERIFY, + HS_CLIENT_KEY_XCHG, + HS_FINISHED = 20 +}; + +typedef struct +{ + uint8_t cipher; + uint8_t key_size; + uint8_t iv_size; + uint8_t key_block_size; + uint8_t padding_size; + uint8_t digest_size; + hmac_func hmac; + crypt_func encrypt; + crypt_func decrypt; +} cipher_info_t; + +struct _SSLObjLoader +{ + uint8_t *buf; + int len; +}; + +typedef struct _SSLObjLoader SSLObjLoader; + +typedef struct +{ + time_t conn_time; + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t master_secret[SSL_SECRET_SIZE]; +} SSL_SESSION; + +typedef struct +{ + uint8_t *buf; + int size; +} SSL_CERT; + +typedef struct +{ + MD5_CTX md5_ctx; + SHA1_CTX sha1_ctx; + uint8_t final_finish_mac[SSL_FINISHED_HASH_SIZE]; + uint8_t *key_block; + uint8_t master_secret[SSL_SECRET_SIZE]; + uint8_t client_random[SSL_RANDOM_SIZE]; /* client's random sequence */ + uint8_t server_random[SSL_RANDOM_SIZE]; /* server's random sequence */ + uint16_t bm_proc_index; +} DISPOSABLE_CTX; + +struct _SSL +{ + uint32_t flag; + uint16_t need_bytes; + uint16_t got_bytes; + uint8_t record_type; + uint8_t cipher; + uint8_t sess_id_size; + int16_t next_state; + int16_t hs_status; + DISPOSABLE_CTX *dc; /* temporary data which we'll get rid of soon */ + int client_fd; + const cipher_info_t *cipher_info; + void *encrypt_ctx; + void *decrypt_ctx; + uint8_t bm_all_data[RT_MAX_PLAIN_LENGTH+RT_EXTRA]; + uint8_t *bm_data; + uint16_t bm_index; + uint16_t bm_read_index; + struct _SSL *next; /* doubly linked list */ + struct _SSL *prev; + struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */ +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t session_index; + SSL_SESSION *session; +#endif +#ifdef CONFIG_SSL_CERT_VERIFICATION + X509_CTX *x509_ctx; +#endif + + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t client_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t server_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t read_sequence[8]; /* 64 bit sequence number */ + uint8_t write_sequence[8]; /* 64 bit sequence number */ + uint8_t hmac_header[SSL_RECORD_SIZE]; /* rx hmac */ +}; + +typedef struct _SSL SSL; + +struct _SSL_CTX +{ + uint32_t options; + uint8_t chain_length; + RSA_CTX *rsa_ctx; +#ifdef CONFIG_SSL_CERT_VERIFICATION + CA_CERT_CTX *ca_cert_ctx; +#endif + SSL *head; + SSL *tail; + SSL_CERT certs[CONFIG_SSL_MAX_CERTS]; +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t num_sessions; + SSL_SESSION **ssl_sessions; +#endif +#ifdef CONFIG_SSL_CTX_MUTEXING + SSL_CTX_MUTEX_TYPE mutex; +#endif +#ifdef CONFIG_OPENSSL_COMPATIBLE + void *bonus_attr; +#endif +}; + +typedef struct _SSL_CTX SSL_CTX; + +/* backwards compatibility */ +typedef struct _SSL_CTX SSLCTX; + +extern const uint8_t ssl_prot_prefs[NUM_PROTOCOLS]; + +SSL *ssl_new(SSL_CTX *ssl_ctx, int client_fd); +void disposable_new(SSL *ssl); +void disposable_free(SSL *ssl); +int send_packet(SSL *ssl, uint8_t protocol, + const uint8_t *in, int length); +int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int process_finished(SSL *ssl, int hs_len); +int process_sslv23_client_hello(SSL *ssl); +int send_alert(SSL *ssl, int error_code); +int send_finished(SSL *ssl); +int send_certificate(SSL *ssl); +int basic_read(SSL *ssl, uint8_t **in_data); +int send_change_cipher_spec(SSL *ssl); +void finished_digest(SSL *ssl, const char *label, uint8_t *digest); +void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret); +void add_packet(SSL *ssl, const uint8_t *pkt, int len); +int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj); +void ssl_obj_free(SSLObjLoader *ssl_obj); +int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int load_key_certs(SSL_CTX *ssl_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx); +#endif +#ifdef CONFIG_SSL_ENABLE_CLIENT +int do_client_connect(SSL *ssl); +#endif + +#ifdef CONFIG_SSL_FULL_MODE +void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok); +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...); +void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx); +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx); +void DISPLAY_ALERT(SSL *ssl, int alert); +#else +#define DISPLAY_STATE(A,B,C,D) +#define DISPLAY_CERT(A,B) +#define DISPLAY_RSA(A,B) +#define DISPLAY_ALERT(A, B) +#ifdef WIN32 +void DISPLAY_BYTES(SSL *ssl, const char *format,/* win32 has no variadic macros */ + const uint8_t *data, int size, ...); +#else +#define DISPLAY_BYTES(A,B,C,D,...) +#endif +#endif + +#ifdef CONFIG_SSL_CERT_VERIFICATION +int process_certificate(SSL *ssl, X509_CTX **x509_ctx); +#endif + +SSL_SESSION *ssl_session_update(int max_sessions, + SSL_SESSION *ssl_sessions[], SSL *ssl, + const uint8_t *session_id); +void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/nixio/axTLS/ssl/tls1_clnt.c b/libs/nixio/axTLS/ssl/tls1_clnt.c new file mode 100644 index 000000000..91314333c --- /dev/null +++ b/libs/nixio/axTLS/ssl/tls1_clnt.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include +#include +#include +#include + +#include "ssl.h" + +#ifdef CONFIG_SSL_ENABLE_CLIENT /* all commented out if no client */ + +static int send_client_hello(SSL *ssl); +static int process_server_hello(SSL *ssl); +static int process_server_hello_done(SSL *ssl); +static int send_client_key_xchg(SSL *ssl); +static int process_cert_req(SSL *ssl); +static int send_cert_verify(SSL *ssl); + +/* + * Establish a new SSL connection to an SSL server. + */ +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + SSL *ssl; + int ret; + + SOCKET_BLOCK(client_fd); /* ensure blocking mode */ + ssl = ssl_new(ssl_ctx, client_fd); + + if (session_id && ssl_ctx->num_sessions) + { + if (sess_id_size > SSL_SESSION_ID_SIZE) /* validity check */ + { + ssl_free(ssl); + return NULL; + } + + memcpy(ssl->session_id, session_id, sess_id_size); + ssl->sess_id_size = sess_id_size; + SET_SSL_FLAG(SSL_SESSION_RESUME); /* just flag for later */ + } + + SET_SSL_FLAG(SSL_IS_CLIENT); + ret = do_client_connect(ssl); + return ssl; +} + +/* + * Process the handshake record. + */ +int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + + /* To get here the state must be valid */ + switch (handshake_type) + { + case HS_SERVER_HELLO: + ret = process_server_hello(ssl); + break; + + case HS_CERTIFICATE: + ret = process_certificate(ssl, &ssl->x509_ctx); + break; + + case HS_SERVER_HELLO_DONE: + if ((ret = process_server_hello_done(ssl)) == SSL_OK) + { + if (IS_SET_SSL_FLAG(SSL_HAS_CERT_REQ)) + { + if ((ret = send_certificate(ssl)) == SSL_OK && + (ret = send_client_key_xchg(ssl)) == SSL_OK) + { + send_cert_verify(ssl); + } + } + else + { + ret = send_client_key_xchg(ssl); + } + + if (ret == SSL_OK && + (ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + } + } + break; + + case HS_CERT_REQ: + ret = process_cert_req(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, hs_len); + disposable_free(ssl); /* free up some memory */ + break; + + case HS_HELLO_REQUEST: + disposable_new(ssl); + ret = do_client_connect(ssl); + break; + } + + return ret; +} + +/* + * Do the handshaking from the beginning. + */ +int do_client_connect(SSL *ssl) +{ + int ret = SSL_OK; + + send_client_hello(ssl); /* send the client hello */ + ssl->bm_read_index = 0; + ssl->next_state = HS_SERVER_HELLO; + ssl->hs_status = SSL_NOT_OK; /* not connected */ + x509_free(ssl->x509_ctx); + + /* sit in a loop until it all looks good */ + while (ssl->hs_status != SSL_OK) + { + ret = basic_read(ssl, NULL); + + if (ret < SSL_OK) + { + if (ret != SSL_ERROR_CONN_LOST) + { + /* let the server know we are dying and why */ + if (send_alert(ssl, ret)) + { + /* something nasty happened, so get rid of it */ + kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl); + } + } + + break; + } + } + + ssl->hs_status = ret; /* connected? */ + return ret; +} + +/* + * Send the initial client hello. + */ +static int send_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + time_t tm = time(NULL); + uint8_t *tm_ptr = &buf[6]; /* time will go here */ + int i, offset; + + buf[0] = HS_CLIENT_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = 0x01; + + /* client random value - spec says that 1st 4 bytes are big endian time */ + *tm_ptr++ = (uint8_t)(((long)tm & 0xff000000) >> 24); + *tm_ptr++ = (uint8_t)(((long)tm & 0x00ff0000) >> 16); + *tm_ptr++ = (uint8_t)(((long)tm & 0x0000ff00) >> 8); + *tm_ptr++ = (uint8_t)(((long)tm & 0x000000ff)); + get_random(SSL_RANDOM_SIZE-4, &buf[10]); + memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + + /* give session resumption a go */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) /* set initially by user */ + { + buf[offset++] = ssl->sess_id_size; + memcpy(&buf[offset], ssl->session_id, ssl->sess_id_size); + offset += ssl->sess_id_size; + CLR_SSL_FLAG(SSL_SESSION_RESUME); /* clear so we can set later */ + } + else + { + /* no session id - because no session resumption just yet */ + buf[offset++] = 0; + } + + buf[offset++] = 0; /* number of ciphers */ + buf[offset++] = NUM_PROTOCOLS*2;/* number of ciphers */ + + /* put all our supported protocols in our request */ + for (i = 0; i < NUM_PROTOCOLS; i++) + { + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl_prot_prefs[i]; + } + + buf[offset++] = 1; /* no compression */ + buf[offset++] = 0; + buf[3] = offset - 4; /* handshake size */ + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Process the server hello. + */ +static int process_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int pkt_size = ssl->bm_index; + int version = (buf[4] << 4) + buf[5]; + int num_sessions = ssl->ssl_ctx->num_sessions; + uint8_t sess_id_size; + int offset, ret = SSL_OK; + + /* check that we are talking to a TLSv1 server */ + if (version != 0x31) + return SSL_ERROR_INVALID_VERSION; + + /* get the server random value */ + memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; /* skip of session id size */ + sess_id_size = buf[offset++]; + + if (num_sessions) + { + ssl->session = ssl_session_update(num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, &buf[offset]); + memcpy(ssl->session->session_id, &buf[offset], sess_id_size); + + /* pad the rest with 0's */ + if (sess_id_size < SSL_SESSION_ID_SIZE) + { + memset(&ssl->session->session_id[sess_id_size], 0, + SSL_SESSION_ID_SIZE-sess_id_size); + } + } + + memcpy(ssl->session_id, &buf[offset], sess_id_size); + ssl->sess_id_size = sess_id_size; + offset += sess_id_size; + + /* get the real cipher we are using */ + ssl->cipher = buf[++offset]; + ssl->next_state = IS_SET_SSL_FLAG(SSL_SESSION_RESUME) ? + HS_FINISHED : HS_CERTIFICATE; + + offset++; // skip the compr + PARANOIA_CHECK(pkt_size, offset); + ssl->dc->bm_proc_index = offset+1; + +error: + return ret; +} + +/** + * Process the server hello done message. + */ +static int process_server_hello_done(SSL *ssl) +{ + ssl->next_state = HS_FINISHED; + return SSL_OK; +} + +/* + * Send a client key exchange message. + */ +static int send_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t premaster_secret[SSL_SECRET_SIZE]; + int enc_secret_size = -1; + + buf[0] = HS_CLIENT_KEY_XCHG; + buf[1] = 0; + + premaster_secret[0] = 0x03; /* encode the version number */ + premaster_secret[1] = 0x01; + get_random(SSL_SECRET_SIZE-2, &premaster_secret[2]); + DISPLAY_RSA(ssl, ssl->x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + enc_secret_size = RSA_encrypt(ssl->x509_ctx->rsa_ctx, premaster_secret, + SSL_SECRET_SIZE, &buf[6], 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + buf[2] = (enc_secret_size + 2) >> 8; + buf[3] = (enc_secret_size + 2) & 0xff; + buf[4] = enc_secret_size >> 8; + buf[5] = enc_secret_size & 0xff; + + generate_master_secret(ssl, premaster_secret); + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, enc_secret_size+6); +} + +/* + * Process the certificate request. + */ +static int process_cert_req(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int ret = SSL_OK; + int offset = (buf[2] << 4) + buf[3]; + int pkt_size = ssl->bm_index; + + /* don't do any processing - we will send back an RSA certificate anyway */ + ssl->next_state = HS_SERVER_HELLO_DONE; + SET_SSL_FLAG(SSL_HAS_CERT_REQ); + ssl->dc->bm_proc_index += offset; + PARANOIA_CHECK(pkt_size, offset); +error: + return ret; +} + +/* + * Send a certificate verify message. + */ +static int send_cert_verify(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int n = 0, ret; + + DISPLAY_RSA(ssl, rsa_ctx); + + buf[0] = HS_CERT_VERIFY; + buf[1] = 0; + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + + /* rsa_ctx->bi_ctx is not thread-safe */ + if (rsa_ctx) + { + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_encrypt(rsa_ctx, dgst, sizeof(dgst), &buf[6], 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n == 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + } + + buf[4] = n >> 8; /* add the RSA size (not officially documented) */ + buf[5] = n & 0xff; + n += 2; + buf[2] = n >> 8; + buf[3] = n & 0xff; + ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, n+4); + +error: + return ret; +} + +#endif /* CONFIG_SSL_ENABLE_CLIENT */ diff --git a/libs/nixio/axTLS/ssl/tls1_svr.c b/libs/nixio/axTLS/ssl/tls1_svr.c new file mode 100644 index 000000000..45b9bec6a --- /dev/null +++ b/libs/nixio/axTLS/ssl/tls1_svr.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +#include +#include +#include + +#include "ssl.h" + +static const uint8_t g_hello_done[] = { HS_SERVER_HELLO_DONE, 0, 0, 0 }; + +static int process_client_hello(SSL *ssl); +static int send_server_hello_sequence(SSL *ssl); +static int send_server_hello(SSL *ssl); +static int send_server_hello_done(SSL *ssl); +static int process_client_key_xchg(SSL *ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION +static int send_certificate_request(SSL *ssl); +static int process_cert_verify(SSL *ssl); +#endif + +/* + * Establish a new SSL connection to an SSL client. + */ +EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + ssl = ssl_new(ssl_ctx, client_fd); + ssl->next_state = HS_CLIENT_HELLO; + +#ifdef CONFIG_SSL_FULL_MODE + if (ssl_ctx->chain_length == 0) + printf("Warning - no server certificate defined\n"); TTY_FLUSH(); +#endif + + return ssl; +} + +/* + * Process the handshake record. + */ +int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + ssl->hs_status = SSL_NOT_OK; /* not connected */ + + /* To get here the state must be valid */ + switch (handshake_type) + { + case HS_CLIENT_HELLO: + if ((ret = process_client_hello(ssl)) == SSL_OK) + ret = send_server_hello_sequence(ssl); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case HS_CERTIFICATE:/* the client sends its cert */ + ret = process_certificate(ssl, &ssl->x509_ctx); + + if (ret == SSL_OK) /* verify the cert */ + { + int cert_res; + cert_res = x509_verify( + ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res); + } + break; + + case HS_CERT_VERIFY: + ret = process_cert_verify(ssl); + add_packet(ssl, buf, hs_len); /* needs to be done after */ + break; +#endif + case HS_CLIENT_KEY_XCHG: + ret = process_client_key_xchg(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, hs_len); + disposable_free(ssl); /* free up some memory */ + break; + } + + return ret; +} + +/* + * Process a client hello message. + */ +static int process_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t *record_buf = ssl->hmac_header; + int pkt_size = ssl->bm_index; + int i, j, cs_len, id_len, offset = 6 + SSL_RANDOM_SIZE; + int version = (record_buf[1] << 4) + record_buf[2]; + int ret = SSL_OK; + + /* should be v3.1 (TLSv1) or better - we'll send in v3.1 mode anyway */ + if (version < 0x31) + { + ret = SSL_ERROR_INVALID_VERSION; + ssl_display_error(ret); + goto error; + } + + memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + + /* process the session id */ + id_len = buf[offset++]; + if (id_len > SSL_SESSION_ID_SIZE) + { + return SSL_ERROR_INVALID_SESSION; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + offset += id_len; + cs_len = (buf[offset]<<8) + buf[offset+1]; + offset += 3; /* add 1 due to all cipher suites being 8 bit */ + + PARANOIA_CHECK(pkt_size, offset); + + /* work out what cipher suite we are going to use */ + for (j = 0; j < NUM_PROTOCOLS; j++) + { + for (i = 0; i < cs_len; i += 2) + { + if (ssl_prot_prefs[j] == buf[offset+i]) /* got a match? */ + { + ssl->cipher = ssl_prot_prefs[j]; + goto do_state; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + +do_state: +error: + return ret; +} + +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE +/* + * Some browsers use a hybrid SSLv2 "client hello" + */ +int process_sslv23_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int bytes_needed = ((buf[0] & 0x7f) << 8) + buf[1]; + int version = (buf[3] << 4) + buf[4]; + int ret = SSL_OK; + + /* we have already read 3 extra bytes so far */ + int read_len = SOCKET_READ(ssl->client_fd, buf, bytes_needed-3); + int cs_len = buf[1]; + int id_len = buf[3]; + int ch_len = buf[5]; + int i, j, offset = 8; /* start at first cipher */ + int random_offset = 0; + + DISPLAY_BYTES(ssl, "received %d bytes", buf, read_len, read_len); + + /* should be v3.1 (TLSv1) or better - we'll send in v3.1 mode anyway */ + if (version < 0x31) + { + return SSL_ERROR_INVALID_VERSION; + } + + add_packet(ssl, buf, read_len); + + /* connection has gone, so die */ + if (bytes_needed < 0) + { + return SSL_ERROR_CONN_LOST; + } + + /* now work out what cipher suite we are going to use */ + for (j = 0; j < NUM_PROTOCOLS; j++) + { + for (i = 0; i < cs_len; i += 3) + { + if (ssl_prot_prefs[j] == buf[offset+i]) + { + ssl->cipher = ssl_prot_prefs[j]; + goto server_hello; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + goto error; + +server_hello: + /* get the session id */ + offset += cs_len - 2; /* we've gone 2 bytes past the end */ +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + /* get the client random data */ + offset += id_len; + + /* random can be anywhere between 16 and 32 bytes long - so it is padded + * with 0's to the left */ + if (ch_len == 0x10) + { + random_offset += 0x10; + } + + memcpy(&ssl->dc->client_random[random_offset], &buf[offset], ch_len); + ret = send_server_hello_sequence(ssl); + +error: + return ret; +} +#endif + +/* + * Send the entire server hello sequence + */ +static int send_server_hello_sequence(SSL *ssl) +{ + int ret; + + if ((ret = send_server_hello(ssl)) == SSL_OK) + { +#ifndef CONFIG_SSL_SKELETON_MODE + /* resume handshake? */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + ssl->next_state = HS_FINISHED; + } + } + else +#endif + if ((ret = send_certificate(ssl)) == SSL_OK) + { +#ifdef CONFIG_SSL_CERT_VERIFICATION + /* ask the client for its certificate */ + if (IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION)) + { + if ((ret = send_certificate_request(ssl)) == SSL_OK) + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CERTIFICATE; + } + } + else +#endif + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CLIENT_KEY_XCHG; + } + } + } + + return ret; +} + +/* + * Send a server hello message. + */ +static int send_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int offset = 0; + + buf[0] = HS_SERVER_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = 0x01; + + /* server random value */ + get_random(SSL_RANDOM_SIZE, &buf[6]); + memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + /* retrieve id from session cache */ + buf[offset++] = SSL_SESSION_ID_SIZE; + memcpy(&buf[offset], ssl->session->session_id, SSL_SESSION_ID_SIZE); + memcpy(ssl->session_id, ssl->session->session_id, SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + offset += SSL_SESSION_ID_SIZE; + } + else /* generate our own session id */ +#endif + { +#ifndef CONFIG_SSL_SKELETON_MODE + buf[offset++] = SSL_SESSION_ID_SIZE; + get_random(SSL_SESSION_ID_SIZE, &buf[offset]); + memcpy(ssl->session_id, &buf[offset], SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + + /* store id in session cache */ + if (ssl->ssl_ctx->num_sessions) + { + memcpy(ssl->session->session_id, + ssl->session_id, SSL_SESSION_ID_SIZE); + } + + offset += SSL_SESSION_ID_SIZE; +#else + buf[offset++] = 0; /* don't bother with session id in skelton mode */ +#endif + } + + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl->cipher; + buf[offset++] = 0; /* no compression */ + buf[3] = offset - 4; /* handshake size */ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Send the server hello done message. + */ +static int send_server_hello_done(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_done, sizeof(g_hello_done)); +} + +/* + * Pull apart a client key exchange message. Decrypt the pre-master key (using + * our RSA private key) and then work out the master key. Initialise the + * ciphers. + */ +static int process_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int premaster_size, secret_length = (buf[2] << 8) + buf[3]; + uint8_t premaster_secret[MAX_KEY_BYTE_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int offset = 4; + int ret = SSL_OK; + + if (rsa_ctx == NULL) + { + ret = SSL_ERROR_NO_CERT_DEFINED; + goto error; + } + + /* is there an extra size field? */ + if ((secret_length - 2) == rsa_ctx->num_octets) + offset += 2; + + PARANOIA_CHECK(pkt_size, rsa_ctx->num_octets+offset); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret, 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (premaster_size != SSL_SECRET_SIZE || + premaster_secret[0] != 0x03 || /* check version is 3.1 (TLS) */ + premaster_secret[1] != 0x01) + { + /* guard against a Bleichenbacher attack */ + memset(premaster_secret, 0, SSL_SECRET_SIZE); + /* and continue - will die eventually when checking the mac */ + } + +#if 0 + print_blob("pre-master", premaster_secret, SSL_SECRET_SIZE); +#endif + + generate_master_secret(ssl, premaster_secret); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + ssl->next_state = IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION) ? + HS_CERT_VERIFY : HS_FINISHED; +#else + ssl->next_state = HS_FINISHED; +#endif +error: + ssl->dc->bm_proc_index += rsa_ctx->num_octets+offset; + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +static const uint8_t g_cert_request[] = { HS_CERT_REQ, 0, 0, 4, 1, 0, 0, 0 }; + +/* + * Send the certificate request message. + */ +static int send_certificate_request(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_cert_request, sizeof(g_cert_request)); +} + +/* + * Ensure the client has the private key by first decrypting the packet and + * then checking the packet digests. + */ +static int process_cert_verify(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + uint8_t dgst_buf[MAX_KEY_BYTE_SIZE]; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + X509_CTX *x509_ctx = ssl->x509_ctx; + int ret = SSL_OK; + int n; + + PARANOIA_CHECK(pkt_size, x509_ctx->rsa_ctx->num_octets+6); + DISPLAY_RSA(ssl, x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[6], dgst_buf, 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n != SHA1_SIZE + MD5_SIZE) + { + ret = SSL_ERROR_INVALID_KEY; + goto end_cert_vfy; + } + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + if (memcmp(dgst_buf, dgst, MD5_SIZE + SHA1_SIZE)) + { + ret = SSL_ERROR_INVALID_KEY; + } + +end_cert_vfy: + ssl->next_state = HS_FINISHED; +error: + return ret; +} + +#endif diff --git a/libs/nixio/axTLS/ssl/version.h b/libs/nixio/axTLS/ssl/version.h new file mode 100644 index 000000000..72260bf19 --- /dev/null +++ b/libs/nixio/axTLS/ssl/version.h @@ -0,0 +1 @@ +#define AXTLS_VERSION "1.2.1" diff --git a/libs/nixio/axTLS/ssl/x509.c b/libs/nixio/axTLS/ssl/x509.c new file mode 100644 index 000000000..37db7f4e8 --- /dev/null +++ b/libs/nixio/axTLS/ssl/x509.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT OWNER 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. + */ + +/** + * @file x509.c + * + * Certificate processing. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto_misc.h" + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Retrieve the signature from a certificate. + */ +static const uint8_t *get_signature(const uint8_t *asn1_sig, int *len) +{ + int offset = 0; + const uint8_t *ptr = NULL; + + if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) + goto end_get_sig; + + if (asn1_sig[offset++] != ASN1_OCTET_STRING) + goto end_get_sig; + *len = get_asn1_length(asn1_sig, &offset); + ptr = &asn1_sig[offset]; /* all ok */ + +end_get_sig: + return ptr; +} + +#endif + +/** + * Construct a new x509 object. + * @return 0 if ok. < 0 if there was a problem. + */ +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) +{ + int begin_tbs, end_tbs; + int ret = X509_NOT_OK, offset = 0, cert_size = 0; + X509_CTX *x509_ctx; + BI_CTX *bi_ctx; + + *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX)); + x509_ctx = *ctx; + + /* get the certificate size */ + asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + begin_tbs = offset; /* start of the tbs */ + end_tbs = begin_tbs; /* work out the end of the tbs */ + asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ + { + if (asn1_version(cert, &offset, x509_ctx)) + goto end_cert; + } + + if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ + asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + /* make sure the signature is ok */ + if (asn1_signature_type(cert, &offset, x509_ctx)) + { + ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; + goto end_cert; + } + + if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || + asn1_validity(cert, &offset, x509_ctx) || + asn1_name(cert, &offset, x509_ctx->cert_dn) || + asn1_public_key(cert, &offset, x509_ctx)) + goto end_cert; + + bi_ctx = x509_ctx->rsa_ctx->bi_ctx; + +#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ + /* use the appropriate signature algorithm (SHA1/MD5/MD2) */ + if (x509_ctx->sig_type == SIG_TYPE_MD5) + { + MD5_CTX md5_ctx; + uint8_t md5_dgst[MD5_SIZE]; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD5_Final(md5_dgst, &md5_ctx); + x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_SHA1) + { + SHA1_CTX sha_ctx; + uint8_t sha_dgst[SHA1_SIZE]; + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_MD2) + { + MD2_CTX md2_ctx; + uint8_t md2_dgst[MD2_SIZE]; + MD2_Init(&md2_ctx); + MD2_Update(&md2_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD2_Final(md2_dgst, &md2_ctx); + x509_ctx->digest = bi_import(bi_ctx, md2_dgst, MD2_SIZE); + } + + offset = end_tbs; /* skip the v3 data */ + if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || + asn1_signature(cert, &offset, x509_ctx)) + goto end_cert; +#endif + + if (len) + { + *len = cert_size; + } + + ret = X509_OK; +end_cert: + +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + printf("Error: Invalid X509 ASN.1 file\n"); + } +#endif + + return ret; +} + +/** + * Free an X.509 object's resources. + */ +void x509_free(X509_CTX *x509_ctx) +{ + X509_CTX *next; + int i; + + if (x509_ctx == NULL) /* if already null, then don't bother */ + return; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + free(x509_ctx->ca_cert_dn[i]); + free(x509_ctx->cert_dn[i]); + } + + free(x509_ctx->signature); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (x509_ctx->digest) + { + bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest); + } +#endif + + RSA_free(x509_ctx->rsa_ctx); + + next = x509_ctx->next; + free(x509_ctx); + x509_free(next); /* clear the chain */ +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Take a signature and decrypt it. + */ +static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp) +{ + int i, size; + bigint *decrypted_bi, *dat_bi; + bigint *bir = NULL; + uint8_t *block = (uint8_t *)alloca(sig_len); + + /* decrypt */ + dat_bi = bi_import(ctx, sig, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + /* convert to a normal block */ + decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp); + + bi_export(ctx, decrypted_bi, block, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + i = 10; /* start at the first possible non-padded byte */ + while (block[i++] && i < sig_len); + size = sig_len - i; + + /* get only the bit we want */ + if (size > 0) + { + int len; + const uint8_t *sig_ptr = get_signature(&block[i], &len); + + if (sig_ptr) + { + bir = bi_import(ctx, sig_ptr, len); + } + } + + /* save a few bytes of memory */ + bi_clear_cache(ctx); + return bir; +} + +/** + * Do some basic checks on the certificate chain. + * + * Certificate verification consists of a number of checks: + * - The date of the certificate is after the start date. + * - The date of the certificate is before the finish date. + * - A root certificate exists in the certificate store. + * - That the certificate(s) are not self-signed. + * - The certificate chain is valid. + * - The signature of the certificate is valid. + */ +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +{ + int ret = X509_OK, i = 0; + bigint *cert_sig; + X509_CTX *next_cert = NULL; + BI_CTX *ctx = NULL; + bigint *mod = NULL, *expn = NULL; + int match_ca_cert = 0; + struct timeval tv; + uint8_t is_self_signed = 0; + + if (cert == NULL) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + + /* a self-signed certificate that is not in the CA store - use this + to check the signature */ + if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) + { + is_self_signed = 1; + ctx = cert->rsa_ctx->bi_ctx; + mod = cert->rsa_ctx->m; + expn = cert->rsa_ctx->e; + } + + gettimeofday(&tv, NULL); + + /* check the not before date */ + if (tv.tv_sec < cert->not_before) + { + ret = X509_VFY_ERROR_NOT_YET_VALID; + goto end_verify; + } + + /* check the not after date */ + if (tv.tv_sec > cert->not_after) + { + ret = X509_VFY_ERROR_EXPIRED; + goto end_verify; + } + + next_cert = cert->next; + + /* last cert in the chain - look for a trusted cert */ + if (next_cert == NULL) + { + if (ca_cert_ctx != NULL) + { + /* go thu the CA store */ + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + if (asn1_compare_dn(cert->ca_cert_dn, + ca_cert_ctx->cert[i]->cert_dn) == 0) + { + /* use this CA certificate for signature verification */ + match_ca_cert = 1; + ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx; + mod = ca_cert_ctx->cert[i]->rsa_ctx->m; + expn = ca_cert_ctx->cert[i]->rsa_ctx->e; + break; + } + + i++; + } + } + + /* couldn't find a trusted cert (& let self-signed errors be returned) */ + if (!match_ca_cert && !is_self_signed) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + } + else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0) + { + /* check the chain */ + ret = X509_VFY_ERROR_INVALID_CHAIN; + goto end_verify; + } + else /* use the next certificate in the chain for signature verify */ + { + ctx = next_cert->rsa_ctx->bi_ctx; + mod = next_cert->rsa_ctx->m; + expn = next_cert->rsa_ctx->e; + } + + /* cert is self signed */ + if (!match_ca_cert && is_self_signed) + { + ret = X509_VFY_ERROR_SELF_SIGNED; + goto end_verify; + } + + /* check the signature */ + cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, + bi_clone(ctx, mod), bi_clone(ctx, expn)); + + if (cert_sig && cert->digest) + { + if (bi_compare(cert_sig, cert->digest) != 0) + ret = X509_VFY_ERROR_BAD_SIGNATURE; + + + bi_free(ctx, cert_sig); + } + else + { + ret = X509_VFY_ERROR_BAD_SIGNATURE; + } + + if (ret) + goto end_verify; + + /* go down the certificate chain using recursion. */ + if (next_cert != NULL) + { + ret = x509_verify(ca_cert_ctx, next_cert); + } + +end_verify: + return ret; +} +#endif + +#if defined (CONFIG_SSL_FULL_MODE) +/** + * Used for diagnostics. + */ +static const char *not_part_of_cert = ""; +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) +{ + if (cert == NULL) + return; + + printf("=== CERTIFICATE ISSUED TO ===\n"); + printf("Common Name (CN):\t\t"); + printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ? + cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + printf("Organization (O):\t\t"); + printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ? + cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + printf("Organizational Unit (OU):\t"); + printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + printf("=== CERTIFICATE ISSUED BY ===\n"); + printf("Common Name (CN):\t\t"); + printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ? + cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + printf("Organization (O):\t\t"); + printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ? + cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + printf("Organizational Unit (OU):\t"); + printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + printf("Not Before:\t\t\t%s", ctime(&cert->not_before)); + printf("Not After:\t\t\t%s", ctime(&cert->not_after)); + printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8); + printf("Sig Type:\t\t\t"); + switch (cert->sig_type) + { + case SIG_TYPE_MD5: + printf("MD5\n"); + break; + case SIG_TYPE_SHA1: + printf("SHA1\n"); + break; + case SIG_TYPE_MD2: + printf("MD2\n"); + break; + default: + printf("Unrecognized: %d\n", cert->sig_type); + break; + } + + if (ca_cert_ctx) + { + printf("Verify:\t\t\t\t%s\n", + x509_display_error(x509_verify(ca_cert_ctx, cert))); + } + +#if 0 + print_blob("Signature", cert->signature, cert->sig_len); + bi_print("Modulus", cert->rsa_ctx->m); + bi_print("Pub Exp", cert->rsa_ctx->e); +#endif + + if (ca_cert_ctx) + { + x509_print(cert->next, ca_cert_ctx); + } + + TTY_FLUSH(); +} + +const char * x509_display_error(int error) +{ + switch (error) + { + case X509_OK: + return "Certificate verify successful"; + + case X509_NOT_OK: + return "X509 not ok"; + + case X509_VFY_ERROR_NO_TRUSTED_CERT: + return "No trusted cert is available"; + + case X509_VFY_ERROR_BAD_SIGNATURE: + return "Bad signature"; + + case X509_VFY_ERROR_NOT_YET_VALID: + return "Cert is not yet valid"; + + case X509_VFY_ERROR_EXPIRED: + return "Cert has expired"; + + case X509_VFY_ERROR_SELF_SIGNED: + return "Cert is self-signed"; + + case X509_VFY_ERROR_INVALID_CHAIN: + return "Chain is invalid (check order of certs)"; + + case X509_VFY_ERROR_UNSUPPORTED_DIGEST: + return "Unsupported digest"; + + case X509_INVALID_PRIV_KEY: + return "Invalid private key"; + + default: + return "Unknown"; + } +} +#endif /* CONFIG_SSL_FULL_MODE */ + diff --git a/libs/nixio/axTLS/www/bin/.htaccess b/libs/nixio/axTLS/www/bin/.htaccess new file mode 100644 index 000000000..4496fa9ed --- /dev/null +++ b/libs/nixio/axTLS/www/bin/.htaccess @@ -0,0 +1,2 @@ +Deny all + diff --git a/libs/nixio/axTLS/www/favicon.ico b/libs/nixio/axTLS/www/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9f6f30e4c6de09f2e93be5e3cbde8a97850b1989 GIT binary patch literal 22486 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 literal 0 HcmV?d00001 diff --git a/libs/nixio/axTLS/www/index.html b/libs/nixio/axTLS/www/index.html new file mode 100755 index 000000000..25cd2e69b --- /dev/null +++ b/libs/nixio/axTLS/www/index.html @@ -0,0 +1,7106 @@ + + + + + + + + + + + + axTLS Embedded SSL - changes, notes and errata + + + + + + + + + + + + + +
+
+
+
+
changes, notes and errata
+
Type the text for 'YourName'
+
@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.1@@\n\n!!__SSL Library__\n* Certificate verification now works for Firefox.\n* Extended the openssl API.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.0@@\n\n!!__SSL Library__\n* A self-signed certificate will be verified as ok provided that that it is on the certificate authority list.\n* Certificates are not verified when added as certificate authorities (since self-signed and expired certificates can be added to browsers etc)\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.9@@\n\n!!__SSL Library__\n* Now support MS IIS resource kit certificates (thanks to Carsten Sørensen).\n* Fixed a memory leak when freeing more than one CA certificate.\n* The bigint library had a problem with squaring which affected classical reduction (thanks to Manuel Klimek).\n\n!!__axhttpd__\n* Brought back setuid()/setgid() as an option.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.8@@\n\n!!__SSL Library__\n* Now using a BSD style license.\n* Self-signed certificates can now be automatically generated (the keys still need to be provided).\n* A new API call //ssl_x509_create()// can be used to programatically create the certificate.\n* Certificate/keys can be loaded automatically given a file location.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.7@@\n\n!!__SSL Library__\n\n* Variable sized session id's is now better handled for session caching. It has meant a new API call //ssl_get_session_id_size()// and a change to //ssl_client_new()// to define the session id size.\n* Muliple records with a single header are now better supported (thanks to Hervé Sibert).\n* ~MD2 added for Verisign root cert verification (thanks to Byron Rakitzis).\n* The ~MD5/~SHA1 digests are calculated incrementally to reduce memory (thanks to Byron Rakitzis).\n* The bigint cache is now cleared regularly to reduce memory.\n\n!!__axhttpd__\n\n* Improved the POST handling (thanks to Christian Melki).\n* CSS files now work properly.\n* Lua's CGI launcher location is configurable.\n* //vfork()// is now used for CGI for performance reasons.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.6@@\n\n!!__SSL Library__\n\n* ~RC4 speed improvements\n* Lua samples/bindings now work properly\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.5@@\n\n!!__SSL Library__\n\n* Session id's can now be variable lengths in server hello messages.\n* 0 length client certificates are now supported.\n* ssl_version() now returns just the version and not the date.\n* ssl_write() was not sending complete packets under load.\n\n!!__axhttpd__\n\n* Completely updated the CGI code.\n* Lua now integrated - Lua scripts and Lua Pages now run.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.4@@\n\n!!__SSL Library__\n\n* Fixed a Win32 crypto library issue with non-Administrator users\n* Removed compiler warnings that showed up in ~FC6.\n* GNU TLS certificates are now accepted.\n* Separated the send/receive headers for HMAC calculations.\n* Fixed a compilation problem with swig/perl/~FC6.\n* Fixed an issue with loading PEM CA certificates.\n\n!!__axhttpd__\n\n* Made //setuid()/setgid()// call an mconf option.\n* Made //chroot()// an mconf option. Default to //chdir()// instead.\n* Removed optional permissions checking.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.1@@\n\n!!__SSL Library__\n\n* AES should now work on 16bit processors (there was an alignment problem).\n* Various freed objects are cleared before freeing.\n* Header files now installed in ///usr/local/include/axTLS//.\n* -DCYGWIN replaced with -~DCONFIG_PLATFORM_CYGWIN (and the same for Solaris).\n* removed "-noextern" option in Swig. Fixed some other warnings in Win32.\n* SSLCTX changed to ~SSL_CTX (to be consistent with openssl). SSLCTX still exists for backwards compatibility.\n* malloc() and friends call abort() on failure.\n* Fixed a memory leak in directory listings.\n* Added openssl() compatibility functions.\n* Fixed Cygwin 'make install' issue.\n\n!!__axhttpd__\n\n* main.c now becomes axhttpd.c.\n* Header file issue fixed (in mime_types.c).\n* //chroot()// now used for better security.\n* Basic authentication implemented (via .htpasswd).\n* SSL access/denial protection implemented (via .htaccess).\n* Directory access protection implemented (via .htaccess).\n* Can now have more than one CGI file extension in mconf.\n* "~If-Modified-Since" request now handled properly.\n* Performance tweaks to remove //ssl_find()//.
+
[[Read Me]]
+
axTLS uses a BSD style license:\n\nCopyright (c) 2008, Cameron Rich All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer. Redistributions in binary\nform must reproduce the above copyright notice, this list of conditions and\nthe following disclaimer in the documentation and/or other materials\nprovided with the distribution. Neither the name of the axTLS Project nor\nthe names of its contributors may be used to endorse or promote products\nderived from this software without specific prior written permission. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.
+
[[Read Me]] \n[[Changelog]]\n[[axhttpd]]\n[[License]]
+
<div class='header' macro='gradient vert #390108 #900'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu'>\n<div refresh='content' tiddler='MainMenu'></div>\n</div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>
+
!@@bgcolor(#ff0000):color(#ffffff):axTLS Quick Start Guide@@\n\nThis is a guide to get a small SSL web-server up and running quickly.\n\n!!__Introduction__\n\nThe axTLS project is an SSL client/server library using the ~TLSv1 protocol. It is designed to be small and fast, and is suited to embedded projects. A web server is included.\n\nThe basic web server + SSL library is around 60-70kB and is configurable for features or size.\n\n!!__Compilation__\n\nAll platforms require GNU make. This means on Win32 that Cygwin needs to be installed with "make" and various developer options selected.\n\nConfiguration now uses a tool called "mconf" which gives a nice way to configure options (similar to what is used in ~BusyBox and the Linux kernel).\n\nYou should be able to compile axTLS simply by extracting it, change into the extracted directory and typing:\n\n{{indent{{{{> make}}}\n\nSelect your platform type, save the configuration, exit, and then type "make" again.\n\nIf all goes well, you should end up with an executable called "axhttpd" (or axhttpd.exe) in the //_stage// directory.\n\nTo play with all the various axTLS options, type:\n\n{{indent{{{{> make menuconfig}}}\n\nSave the new configuration and rebuild.\n\n!!__Running it__\n\nTo run it, go to the //_stage// directory, and type (as superuser):\n\n{{indent{{{{> axhttpd}}}\n\nNote: you may have to set your ~LD_LIBRARY_PATH - e.g. go to //_stage// and type //export ~LD_LIBRARY_PATH=`pwd`//\n\nAnd then point your browser at https://127.0.0.1 And you should see a this html page with a padlock appearing on your browser. or type http://127.0.0.1 to see the same page unencrypted.\n\n!!__The axssl utilities__\n\nThe axssl suite of tools are the SSL test tools in the various language bindings. They are:\n\n* axssl - C sample\n* axssl.csharp - C# sample\n* axssl.vbnet - VB.NET sample\n* axtls.jar - Java sample\n* axssl.pl - Perl sample\n* axssl.lua - Lua sample\n\nAll the tools have identical command-line parameters. e.g. to run something interesting:\n\n{{indent{{{{> axssl s_server -verify -CAfile ../ssl/test/axTLS.ca_x509}}}\n\nand\n\n{{indent{{{{> axssl s_client -cert ../ssl/test/axTLS.x509_1024 -key ../ssl/test/axTLS.key_1024 -reconnect}}}\n\n!!!!C#\n\nIf building under Linux or other non-Win32 platforms, Mono must be installed and the executable is run as:\n\n{{indent{{{{> mono axssl.csharp.exe ...}}}\n\n!!!!Java\n\nThe java version is run as:\n\n{{indent{{{{> java -jar axtls.jar <options>}}}\n\n!!!!Perl\n\n{{indent{{{{> [perl] ./axssl.pl <options>}}}\n\nIf running under Win32, be sure to use the correct version of Perl (i.e. ~ActiveState's version works ok).\n\n!!!!Lua\n\n{{indent{{{{> [lua] ./axssl.lua <options>}}}\n\n!__Known Issues__\n\n* Firefox doesn't handle legacy ~SSLv2 at all well. Disabling ~SSLv2 still initiates a ~SSLv23 handshake (v1.5). And continuous pressing of the "Reload" page instigates a change to ~SSLv3 for some reason (even though the TLS 1.0 option is selected). This will cause a "Firefox and <server> cannot communicate securely because they have no common encryption algorithms" (v1.5), or "Firefox can't connect to <server> because the site uses a security protocol which isn't enabled" (v2.0). See bugzilla issues 343543 and 359484 (Comment #7). It's all broken (hopefully fixed soon).\n* Perl/Java bindings don't work on 64 bit Linux machines. I can't even compile the latest version of Perl on an ~AMD64 box (using ~FC3).\n* Java 1.4 or better is required for the Java interfaces.\n* Processes that fork can't use session resumption unless some form of IPC is used.\n* Ensure libperl.so and libaxtls.so are in the shared library path when running with the perl bindings. A way to do this is with:\n\n{{indent{{{{> export LD_LIBRARY_PATH=`perl -e 'use Config; print $Config{archlib};'`/CORE:.}}}\n* The lua sample requires the luabit library from http://luaforge.net/projects/bit.\n\n!!!!Win32 issues\n\n* Be careful about doing .NET executions on network drives - .NET complains with security exceptions on the binary. //TODO: Add a manifest file to prevent this.//\n* CGI has been removed from Win32 - it needs a lot more work to get it right.\n* The default Microsoft .NET SDK is v2.0.50727. Download from: http://msdn.microsoft.com/netframework/downloads/updates/default.aspx.\n\n!!!!Solaris issues\n\n* mconf doesn't work well - some manual tweaking is required for string values.\n* GNU make is required and needs to be in $PATH.\n* To get swig's library dependencies to work (and for the C library to be found), I needed to type:\n\n{{indent{{{{> export LD_LIBRARY_PATH=/usr/local/gcc-3.3.1/lib:.}}}\n\n!!!!Cygwin issues\n\n* The bindings all compile but don't run under Cygwin with the exception of Perl. This is due to win32 executables being incompatible with Cygwin libraries.\n\n
+
changes, notes and errata
+
axTLS Embedded SSL
+
http://axtls.cerocclub.com.au
+
/***\nhttp://tiddlystyles.com/#theme:DevFire\nAuthor: Clint Checketts\n***/\n\n/*{{{*/\nbody {\nbackground: #000;\n}\n/*}}}*/\n/***\n!Link styles /% ============================================================= %/\n***/\n/*{{{*/\na,\na.button,\n#mainMenu a.button,\n#sidebarOptions .sliderPanel a{\n color: #ffbf00;\n border: 0;\n background: transparent;\n}\n\na:hover,\na.button:hover,\n#mainMenu a.button:hover,\n#sidebarOptions .sliderPanel a:hover\n#sidebarOptions .sliderPanel a:active{\n color: #ff7f00;\n border: 0;\n border-bottom: #ff7f00 1px dashed;\n background: transparent;\n text-decoration: none;\n}\n\n#displayArea .button.highlight{\n color: #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Header styles /% ============================================================= %/\n***/\n/*{{{*/\n.header{\n border-bottom: 2px solid #ffbf00;\n color: #fff;\n}\n\n.headerForeground a {\n color: #fff;\n}\n\n.header a:hover {\n border-bottom: 1px dashed #fff;\n}\n/*}}}*/\n/***\n!Main menu styles /% ============================================================= %/\n***/\n/*{{{*/\n#mainMenu {color: #fff;}\n#mainMenu h1{\n font-size: 1.1em;\n}\n#mainMenu li,#mainMenu ul{\n list-style: none;\n margin: 0;\n padding: 0;\n}\n/*}}}*/\n/***\n!Sidebar styles /% ============================================================= %/\n***/\n/*{{{*/\n#sidebar {\n right: 0;\n color: #fff;\n border: 2px solid #ffbf00;\n border-width: 0 0 2px 2px;\n}\n#sidebarOptions {\n background-color: #4c4c4c;\n padding: 0;\n}\n\n#sidebarOptions a{\n margin: 0;\n color: #ffbf00;\n border: 0;\n}\n#sidebarOptions a:hover {\n color: #4c4c4c;\n background-color: #ffbf00;\n\n}\n\n#sidebarOptions a:active {\n color: #ffbf00;\n background-color: transparent;\n}\n\n#sidebarOptions .sliderPanel {\n background-color: #333;\n margin: 0;\n}\n\n#sidebarTabs {background-color: #4c4c4c;}\n#sidebarTabs .tabSelected {\n padding: 3px 3px;\n cursor: default;\n color: #ffbf00;\n background-color: #666;\n}\n#sidebarTabs .tabUnselected {\n color: #ffbf00;\n background-color: #5f5f5f;\n padding: 0 4px;\n}\n\n#sidebarTabs .tabUnselected:hover,\n#sidebarTabs .tabContents {\n background-color: #666;\n}\n\n.listTitle{color: #FFF;}\n#sidebarTabs .tabContents a{\n color: #ffbf00;\n}\n\n#sidebarTabs .tabContents a:hover{\n color: #ff7f00;\n background: transparent;\n}\n\n#sidebarTabs .txtMoreTab .tabSelected,\n#sidebarTabs .txtMoreTab .tab:hover,\n#sidebarTabs .txtMoreTab .tabContents{\n color: #ffbf00;\n background: #4c4c4c;\n}\n\n#sidebarTabs .txtMoreTab .tabUnselected {\n color: #ffbf00;\n background: #5f5f5f;\n}\n\n.tab.tabSelected, .tab.tabSelected:hover{color: #ffbf00; border: 0; background-color: #4c4c4c;cursor:default;}\n.tab.tabUnselected {background-color: #666;}\n.tab.tabUnselected:hover{color:#ffbf00; border: 0;background-color: #4c4c4c;}\n.tabContents {\n background-color: #4c4c4c;\n border: 0;\n}\n.tabContents .tabContents{background: #666;}\n.tabContents .tabSelected{background: #666;}\n.tabContents .tabUnselected{background: #5f5f5f;}\n.tabContents .tab:hover{background: #666;}\n/*}}}*/\n/***\n!Message area styles /% ============================================================= %/\n***/\n/*{{{*/\n#messageArea {background-color: #666; color: #fff; border: 2px solid #ffbf00;}\n#messageArea a:link, #messageArea a:visited {color: #ffbf00; text-decoration:none;}\n#messageArea a:hover {color: #ff7f00;}\n#messageArea a:active {color: #ff7f00;}\n#messageArea .messageToolbar a{\n border: 1px solid #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Popup styles /% ============================================================= %/\n***/\n/*{{{*/\n.popup {color: #fff; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n.popup li.disabled{color: #fff;}\n.popup a {color: #ffbf00; }\n.popup a:hover { background: transparent; color: #ff7f00; border: 0;}\n.popup hr {color: #ffbf00; background: #ffbf00;}\n/*}}}*/\n/***\n!Tiddler Display styles /% ============================================================= %/\n***/\n/*{{{*/\n.title{color: #fff;}\nh1, h2, h3, h4, h5 {\n color: #fff;\n background-color: transparent;\n border-bottom: 1px solid #333;\n}\n\n.subtitle{\n color: #666;\n}\n\n.viewer {color: #fff; }\n\n.viewer table{background: #666; color: #fff;}\n\n.viewer th {background-color: #996; color: #fff;}\n\n.viewer pre, .viewer code {color: #ddd; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n\n.viewer hr {color: #666;}\n\n.tiddler .button {color: #4c4c4c;}\n.tiddler .button:hover { color: #ffbf00; background-color: #4c4c4c;}\n.tiddler .button:active {color: #ffbf00; background-color: #4c4c4c;}\n\n.toolbar {\n color: #4c4c4c;\n}\n\n.toolbar a.button,\n.toolbar a.button:hover,\n.toolbar a.button:active,\n.editorFooter a{\n border: 0;\n}\n\n.footer {\n color: #ddd;\n}\n\n.selected .footer {\n color: #888;\n}\n\n.highlight, .marked {\n color: #000;\n background-color: #ffe72f;\n}\n.editorFooter {\n color: #aaa;\n}\n\n.tab{\n-moz-border-radius-topleft: 3px;\n-moz-border-radius-topright: 3px;\n}\n\n.tagging,\n.tagged{\n background: #4c4c4c;\n border: 1px solid #4c4c4c; \n}\n\n.selected .tagging,\n.selected .tagged{\n background-color: #333;\n border: 1px solid #ffbf00;\n}\n\n.tagging .listTitle,\n.tagged .listTitle{\n color: #fff;\n}\n\n.tagging .button,\n.tagged .button{\n color: #ffbf00;\n border: 0;\n padding: 0;\n}\n\n.tagging .button:hover,\n.tagged .button:hover{\nbackground: transparent;\n}\n\n.selected .isTag .tagging.simple,\n.selected .tagged.simple,\n.isTag .tagging.simple,\n.tagged.simple {\n float: none;\n display: inline;\n border: 0;\n background: transparent;\n color: #fff;\n margin: 0;\n}\n\n.cascade {\n background: #4c4c4c;\n color: #ddd;\n border: 1px solid #ffbf00;\n}\n/*}}}*/
+
axhttpd is a small embedded web server using the axTLS library. It is based originally on the web server written by Doug Currie which is at http://www.hcsw.org/awhttpd.\n\n!@@bgcolor(#ff0000):color(#ffffff):axhttpd Features@@ \n\n!!__Basic Authentication__\n\nBasic Authentication uses a password file called ".htpasswd", in the directory to be protected. This file is formatted as the familiar colon-separated username/encrypted-password pair, records delimited by newlines. The protection does not carry over to subdirectories. The utility program htpasswd is included to help manually edit .htpasswd files.\n\nThe encryption of this password uses a proprietary algorithm due to the dependency of many crypt libraries on DES. An example is in [[/test_dir/no_http|https://127.0.0.1/test_dir/no_http]] (username 'abcd', password is '1234').\n\n//Note: This is an mconf enabled configuration option.//\n\n!!__SSL Protection__\n\nDirectories/files can be accessed using the 'http' or 'https' uri prefix. If normal http access for a directory needs to be disabled, then put "~SSLRequireSSL" into a '.htaccess' file in the directory to be protected. \n\nConversely, use "~SSLDenySSL" to deny access to directories via SSL.\n\nAn example is in [[/test_dir/no_http|http://127.0.0.1/test_dir/no_http]] and [[/test_dir/no_ssl|https://127.0.0.1/test_dir/no_ssl]].\n\nEntire directories can be denied access with a "Deny all" directive (regardless of SSL or authentication). An example is in [[/test_dir/bin|http://127.0.0.1/test_dir/bin]]\n\n!!__CGI__\n\nMost of the CGI 1.1 variables are now placed into the script environment and should work as normal.\n\n!!__Lua and Lua Pages__\n\nThis is a small scripting language gaining popularity in embedded applications due to its small footprint and fast speed.\n\nLua has been incorporated into the build, so simply select it and it will automatically install. Try pointing your browser at [[test_main.html]|http://127.0.0.1/lua/test_main.html]] to see an example of Lua Pages.\n\n//Note: This is an mconf enabled configuration option.//\n\n!!__Directory Listing__\n\nAn mconf option. Allow the files in directories to be displayed. An example is in [[/test_dir|http://127.0.0.1/test_dir]]\n\n!!__Other Features__\n\n* Timeout - HTTP 1.1 allows for persistent connections. This is the time allowed for this connection in seconds.\n* Daemon - Puts the process in daemon mode. \n* SSL session cache size - The size of the session cache (a heavily loaded server should maintain many sessions). A session will save on expensive SSL handshaking.\n\n
+
+ + + + + diff --git a/libs/nixio/axTLS/www/lua/download.lua b/libs/nixio/axTLS/www/lua/download.lua new file mode 100644 index 000000000..2ee1a71e9 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/download.lua @@ -0,0 +1,75 @@ +#!/usr/local/bin/lua + +require"luasocket" + +function receive (connection) + connection:settimeout(0) + local s, status = connection:receive (2^10) + if status == "timeout" then + coroutine.yield (connection) + end + return s, status +end + +function download (host, file, outfile) + --local f = assert (io.open (outfile, "w")) + local c = assert (socket.connect (host, 80)) + c:send ("GET "..file.." HTTP/1.0\r\n\r\n") + while true do + local s, status = receive (c) + --f:write (s) + if status == "closed" then + break + end + end + c:close() + --f:close() +end + +local threads = {} +function get (host, file, outfile) + print (string.format ("Downloading %s from %s to %s", file, host, outfile)) + local co = coroutine.create (function () + return download (host, file, outfile) + end) + table.insert (threads, co) +end + +function dispatcher () + while true do + local n = table.getn (threads) + if n == 0 then + break + end + local connections = {} + for i = 1, n do + local status, res = coroutine.resume (threads[i]) + if not res then + table.remove (threads, i) + break + else + table.insert (connections, res) + end + end + if table.getn (connections) == n then + socket.select (connections) + end + end +end + +local url = arg[1] +if not url then + print (string.format ("usage: %s url [times]", arg[0])) + os.exit() +end +local times = arg[2] or 5 + +url = string.gsub (url, "^http.?://", "") +local _, _, host, file = string.find (url, "^([^/]+)(/.*)") +local _, _, fn = string.find (file, "([^/]+)$") + +for i = 1, times do + get (host, file, fn..i) +end + +dispatcher () diff --git a/libs/nixio/axTLS/www/lua/env.lua b/libs/nixio/axTLS/www/lua/env.lua new file mode 100644 index 000000000..c4298477b --- /dev/null +++ b/libs/nixio/axTLS/www/lua/env.lua @@ -0,0 +1,26 @@ +-- This file should be executed before any script in this directory +-- according to the configuration (cgilua/conf.lua). + +pcall (cgilua.enablesession) + +local put, mkurlpath = cgilua.put, cgilua.mkurlpath + +cgilua.addclosefunction (function () + put [[ +

CGILua installation overview

+ + + + + + + + + + + + + +
Version + Copyright + Description +
<%= tostring(_G[l[2]]) %> + + +
<%= idx(p,"VERSION") %> + <%= idx(p,"COPYRIGHT") %> + <%= idx(p,"DESCRIPTION") %> +
+ + + diff --git a/libs/nixio/axTLS/www/lua/prepara_sql2.lua b/libs/nixio/axTLS/www/lua/prepara_sql2.lua new file mode 100644 index 000000000..6a37c2fef --- /dev/null +++ b/libs/nixio/axTLS/www/lua/prepara_sql2.lua @@ -0,0 +1,31 @@ +#!/usr/local/bin/lua + +MAX_ROWS = arg[1] or 10 + +require"postgres" + +local env = assert (luasql.postgres ()) +local conn = assert (env:connect ("luasql-test", "tomas")) + +-- Apaga restos de outros testes. +conn:execute "drop table t2" +conn:execute "drop table t1" + +-- Criando as tabelas. +assert (conn:execute [[create table t1 ( + a int, + b int +)]]) +assert (conn:execute [[create table t2 ( + c int, + d int +)]]) + +-- Preenchedo as tabelas. +for i = 1, MAX_ROWS do + local ii = 2*i + assert (conn:execute (string.format ([[ +insert into t1 values (%d, %d); +insert into t2 values (%d, %d);]], + ii, i, ii, i))) +end diff --git a/libs/nixio/axTLS/www/lua/test_conc.lua b/libs/nixio/axTLS/www/lua/test_conc.lua new file mode 100644 index 000000000..bbb9be784 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_conc.lua @@ -0,0 +1,38 @@ +cgilua.htmlheader() +if ap then + local pid, ppid = ap.pid () + if not ppid then + ppid = "no parent pid" + end + cgilua.put ("pid = "..pid.." ("..ppid..")".."\n") +end + +assert(type(stable.get) == "function") +assert(type(stable.set) == "function") + +cgilua.put"stable.pairs = {
\n" +for i, v in stable.pairs () do + cgilua.put (i.." = "..tostring(v).."
\n") +end +cgilua.put"}
\n" + +local counter = stable.get"counter" or 0 +stable.set ("counter", counter + 1) + +local f = stable.get"f" +if not f then + local d = os.date() + stable.set ("f", function () return d end) +else + cgilua.put ("f() = "..tostring (f ())) +end + +cgilua.put"
\n" +for i = 1,800 do + cgilua.put (i) + for ii = 1,1000 do + cgilua.put ("") + end + cgilua.put ("\n") +end +cgilua.put ("End") diff --git a/libs/nixio/axTLS/www/lua/test_cookies.lp b/libs/nixio/axTLS/www/lua/test_cookies.lp new file mode 100644 index 000000000..932b9c5e9 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_cookies.lp @@ -0,0 +1,13 @@ + + +

Testing Cookies library

+ +<%= CL_COOKIE%> = <%= tostring(test)%>
+Assigning current date to cookie!
+Reload this script to check cookie's value! diff --git a/libs/nixio/axTLS/www/lua/test_cookies.lua b/libs/nixio/axTLS/www/lua/test_cookies.lua new file mode 100644 index 000000000..6af935e89 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_cookies.lua @@ -0,0 +1,14 @@ +local cookies = require"cgilua.cookies" +CL_COOKIE = "cgilua_cookie" + +local test = cookies.get (CL_COOKIE) +cookies.set (CL_COOKIE, os.date()) + +cgilua.htmlheader () +cgilua.put ([[ +

Testing Cookies library

+ +]]..CL_COOKIE..' = '..tostring(test)..[[
+Assigning current date to cookie!
+Reload this script to check cookie's value! +]]) diff --git a/libs/nixio/axTLS/www/lua/test_err.lua b/libs/nixio/axTLS/www/lua/test_err.lua new file mode 100644 index 000000000..4d6ffc970 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_err.lua @@ -0,0 +1,4 @@ +cgilua.htmlheader() +cgilua.put"Oi!" +--io.write"something\n" +cgilua.errorlog ("eca", "emerg") diff --git a/libs/nixio/axTLS/www/lua/test_fs.lua b/libs/nixio/axTLS/www/lua/test_fs.lua new file mode 100644 index 000000000..566ed8b20 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_fs.lua @@ -0,0 +1,23 @@ +function link_dir (dir, base) + local path = base.."/"..dir + local mode = lfs.attributes (path).mode + if mode == "directory" then + return string.format ('%s', + cgilua.mkurlpath ("test_fs.lua", { dir = path }), + dir) + else + return dir + end +end + +cgilua.htmlheader () +cgilua.put ("

Testing Filesystem library

\n") +cgilua.put ("\n") +cgilua.put ("\n") +local i = 0 +local dir = cgi.dir or "." +for file in lfs.dir (dir) do + i = i+1 + cgilua.put ("\n") +end +cgilua.put ("
Testing dir
"..i..""..link_dir(file, dir).."
\n") diff --git a/libs/nixio/axTLS/www/lua/test_htk.lua b/libs/nixio/axTLS/www/lua/test_htk.lua new file mode 100644 index 000000000..ac1de6c31 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_htk.lua @@ -0,0 +1,22 @@ +require"htk" + +local a_table = {} +for i = 1, 20 do + local l = {} + for j = 1, 20 do + table.insert (l, HTK.TD { "cell "..i..","..j }) + end + table.insert (a_table, HTK.TR (l)) +end + +cgilua.htmlheader() +cgilua.put (HTK.HTML { + HTK.HEAD { HTK.TITLE { "Titulo da Pagina" } }, + HTK.BODY { + bgcolor = "#FFFFFF", + HTK.H1 { "Titulo da Pagina" }, + HTK.P {}, + "Uma página qualquer", + HTK.TABLE (a_table), + } +}) diff --git a/libs/nixio/axTLS/www/lua/test_lib.lua b/libs/nixio/axTLS/www/lua/test_lib.lua new file mode 100644 index 000000000..504125372 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_lib.lua @@ -0,0 +1,31 @@ +local function getfield (t, f) + for w in string.gfind(f, "[%w_]+") do + if not t then return nil end + t = t[w] + end + return t +end + +function test_lib (libname) + local ok, err = pcall (require, libname) + if not ok then + cgilua.put ("Library "..libname.." not found
\n".. + err) + else + cgilua.put ("Library "..libname.."
\n") + local t = getfield (_G, libname) + if type(t) ~= "table" then + cgilua.put (tostring(t)) + else + for i, v in pairs (t) do + cgilua.put ("  "..tostring(i).." = "..tostring(v).."
\n") + end + end + end + cgilua.put ("\n

\n") +end + +cgilua.htmlheader () +for _, lib in ipairs { "lfs", "socket", "luasql.postgres", "luasql", "lxp", "lxp.lom", "lualdap", "htk", "xmlrpc", "xmlrpc.http" } do + test_lib (lib) +end diff --git a/libs/nixio/axTLS/www/lua/test_main.html b/libs/nixio/axTLS/www/lua/test_main.html new file mode 100644 index 000000000..a50dd639c --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_main.html @@ -0,0 +1,127 @@ + +Test Page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GET: +
Lua scriptmodule
HTML templatemodule
POST: +
+
+ + + + + + + + + + + + +
+ field 1:
+ field 2:
+ field 3: + op 1 + op 2 + op 3 +
Lua script + module +
HTML template + module +
+
+
POST (with upload): +
+
+ + + + + + + + + + + + +
+ field 1:
+ file (binary!):
+
Lua script + module +
HTML template + module +
+
+
Cookies: +
Lua scriptmodule
HTML templatemodule
Filesystem: +
Lua scriptmodule
Session: +
Lua scriptmodule
CGI Variables: +
HTML templatemodule
Library Overview: +
HTML templatemodule
Concurrency +
Lua scriptmodule
+ diff --git a/libs/nixio/axTLS/www/lua/test_main.lp b/libs/nixio/axTLS/www/lua/test_main.lp new file mode 100644 index 000000000..917ee1e53 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_main.lp @@ -0,0 +1,31 @@ + +Embeded Lua Test + + +cgi = { +\n", vv, a, tostring(b)) + end + v = vv.."}" + end +?> +<%= i %> = <%= tostring(v) %>
+<% +end +%> +} +
+Remote address: <%= cgilua.servervariable"REMOTE_ADDR" %> +
+Is persistent = <%= tostring (SAPI.Info.ispersistent) %> +
+ap =
+lfcgi = <% = tostring(lfcgi) %>
+<%= (ap and ap.handler()) or "" %>
+ + + diff --git a/libs/nixio/axTLS/www/lua/test_main.lua b/libs/nixio/axTLS/www/lua/test_main.lua new file mode 100644 index 000000000..0e997a2d5 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_main.lua @@ -0,0 +1,46 @@ +cgilua.htmlheader() +cgilua.put[[ + +Script Lua Test + + +cgi = { +]] + +for i,v in pairs (cgi) do + if type(v) == "table" then + local vv = "{" + for a,b in pairs(v) do + vv = string.format ("%s%s = %s
\n", vv, a, tostring(b)) + end + v = vv.."}" + end + cgilua.put (string.format ("%s = %s
\n", i, tostring(v))) +end +cgilua.put "}
\n" +cgilua.put ("Remote address: "..cgilua.servervariable"REMOTE_ADDR") +cgilua.put "
\n" +cgilua.put ("Is persistent = "..tostring (SAPI.Info.ispersistent).."
\n") +cgilua.put ("ap="..tostring(ap).."
\n") +cgilua.put ("lfcgi="..tostring(lfcgi).."
\n") + +-- Checking Virtual Environment +local my_output = cgilua.put +cgilua.put = nil +local status, err = pcall (function () + assert (cgilua.put == nil, "cannot change cgilua.put value") +end) +cgilua.put = my_output +assert (status == true, err) + +-- Checking require +local status, err = pcall (function () require"unknown_module" end) +assert (status == false, "unknown_module loaded!") +-- assert (package == nil, "Access to package table allowed!") + +cgilua.put[[ +

+ + +]] +cgilua = nil diff --git a/libs/nixio/axTLS/www/lua/test_session.lua b/libs/nixio/axTLS/www/lua/test_session.lua new file mode 100644 index 000000000..d97cc4520 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_session.lua @@ -0,0 +1,43 @@ +cgilua.enablesession () + +function pt (tab) + for i, v in pairs (tab) do + local vv = v + if type(v) == "table" then + vv = "" + for _i, _v in pairs (v) do + vv = vv..string.format ("%s = %q, ", _i, _v) + end + vv = '{'..vv..'}' + end + cgilua.put (string.format ("%s = %s
\n", tostring (i), tostring (vv))) + end +end + + +if cgi.field then + if not cgilua.session.data.field then + cgilua.session.data.field = {} + end + table.insert (cgilua.session.data.field, cgi.field) +end +cgilua.htmlheader() +if cgilua.session then + cgilua.put "cgi = {
\n" + pt (cgi) + cgilua.put "}
\n" + cgilua.put "cgilua.session.data = {
\n" + pt (cgilua.session.data) + cgilua.put "}
\n" + + cgilua.put [[

+ field:
+
+
]] +else + cgilua.put "Sessions library is not available or not well configured" +end diff --git a/libs/nixio/axTLS/www/lua/test_sql.lua b/libs/nixio/axTLS/www/lua/test_sql.lua new file mode 100644 index 000000000..085ce978c --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_sql.lua @@ -0,0 +1,13 @@ +local s = require"luasql.postgres" + +local env = assert (luasql.postgres ()) +local conn = assert (env:connect ("luasql-test", "tomas")) +local cur = assert (conn:execute ("select count(*) from fetch_test")) + +cgilua.htmlheader() +cgilua.put ("Total lines at table fetch_test is "..cur:fetch()) +cgilua.put (string.format ("
\n%s == %s
\n", tostring(s), tostring(luasql))) + +cur:close() +conn:close() +env:close() diff --git a/libs/nixio/axTLS/www/lua/test_sql2.lua b/libs/nixio/axTLS/www/lua/test_sql2.lua new file mode 100644 index 000000000..a2f6ee12d --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_sql2.lua @@ -0,0 +1,24 @@ +require"postgres" + +local env = assert (luasql.postgres ()) +local conn = assert (env:connect ("luasql-test", "tomas")) +local cur = assert (conn:execute ("select count(*) from t1")) +local total = tonumber (cur:fetch()) +cur:close() +local aleatorio = math.random(total) +local cur = assert (conn:execute ("select * from t1, t2 where b = d and a != "..2*aleatorio)) + +cgilua.htmlheader() +cgilua.put ("Aleatorio = "..aleatorio.."
\n") + +local a,b,c,d = cur:fetch() +cgilua.put ("\n") +while a do +-- cgilua.put ("") + a,b,c,d = cur:fetch() +end +cgilua.put ("
",a,"",b,"",c,"",d,"
\n") + +cur:close() +conn:close() +env:close() diff --git a/libs/nixio/axTLS/www/lua/test_variables.lp b/libs/nixio/axTLS/www/lua/test_variables.lp new file mode 100644 index 000000000..c1ac332f0 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/test_variables.lp @@ -0,0 +1,14 @@ + +<% for _, var in pairs { "SERVER_SOFTWARE", "SERVER_NAME", "GATEWAY_INTERFACE", "SERVER_PROTOCOL", "SERVER_PORT", "REQUEST_METHOD", "PATH_INFO", "PATH_TRANSLATED", "SCRIPT_NAME", "QUERY_STRING", "REMOTE_HOST", "REMOTE_ADDR", "AUTH_TYPE", "REMOTE_USER", "REMOTE_IDENT", "CONTENT_TYPE", "CONTENT_LENGTH", "HTTP_REFERER", "HTTP_COOKIE", "SCRIPT_FILENAME", "DOCUMENT_ROOT", } do %> + +<% end %> +
<%= var %>="<%= cgilua.servervariable(var) or "not defined"%>"
+ +

+ +<% for _, var in ipairs { "script_file", "script_path", "script_pdir", "script_vdir", "script_vpath", "urlpath", } do %> + +<% end %> +
<%= var %>="<%= cgilua[var] %>"
+ +

diff --git a/libs/nixio/axTLS/www/test_dir/bin/.htaccess b/libs/nixio/axTLS/www/test_dir/bin/.htaccess new file mode 100644 index 000000000..8fd8c463a --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/bin/.htaccess @@ -0,0 +1 @@ +Deny all diff --git a/libs/nixio/axTLS/www/test_dir/no_http/.htaccess b/libs/nixio/axTLS/www/test_dir/no_http/.htaccess new file mode 100644 index 000000000..3e20076a2 --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/no_http/.htaccess @@ -0,0 +1 @@ +SSLRequireSSL diff --git a/libs/nixio/axTLS/www/test_dir/no_http/.htpasswd b/libs/nixio/axTLS/www/test_dir/no_http/.htpasswd new file mode 100644 index 000000000..0471b0140 --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/no_http/.htpasswd @@ -0,0 +1,2 @@ +abcd:CQhgDPyy0rvEU8OMxnQIvg==$YdJfIKZimFLYxPf/rbnhtQ== +yaya:Syuss5jE2FNGVdr0kKGoHg==$WLw/SgHZFuAoOuml3GTJVw== diff --git a/libs/nixio/axTLS/www/test_dir/no_http/index.html b/libs/nixio/axTLS/www/test_dir/no_http/index.html new file mode 100644 index 000000000..8b86eba8e --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/no_http/index.html @@ -0,0 +1,6 @@ + +axhttpd is running + +Looks like you got to this directory. + + diff --git a/libs/nixio/axTLS/www/test_dir/no_ssl/.htaccess b/libs/nixio/axTLS/www/test_dir/no_ssl/.htaccess new file mode 100644 index 000000000..d980d265a --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/no_ssl/.htaccess @@ -0,0 +1 @@ +SSLDenySSL diff --git a/libs/nixio/axTLS/www/test_dir/no_ssl/index.html b/libs/nixio/axTLS/www/test_dir/no_ssl/index.html new file mode 100644 index 000000000..8b86eba8e --- /dev/null +++ b/libs/nixio/axTLS/www/test_dir/no_ssl/index.html @@ -0,0 +1,6 @@ + +axhttpd is running + +Looks like you got to this directory. + + -- 2.11.0

+ +Main]] + for _, test in { + { "Get", "test_main.lua", {ab = "cd", ef = "gh"} }, + { "Cookies", "test_cookies.lua", }, + { "FileSystem", "test_fs.lua", }, + { "Libraries", "test_lib.lua", }, + { "Session", "test_session.lua", }, + { "Variables", "test_variables.lp", }, + } do + put (string.format (' · %s', + mkurlpath (test[2], test[3]), test[1])) + end + put [[ +]] +end) diff --git a/libs/nixio/axTLS/www/lua/overview.lp b/libs/nixio/axTLS/www/lua/overview.lp new file mode 100644 index 000000000..4d17002a0 --- /dev/null +++ b/libs/nixio/axTLS/www/lua/overview.lp @@ -0,0 +1,64 @@ + + +CGILua installation overview + + +