From 4ef73546aaf05b811009180b3f5cc58d81a6eb6f Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Mon, 7 Nov 2022 23:20:45 +0100 Subject: [PATCH] mbedsock: Init --- README.md | 8 + flake.nix | 29 ++ mbedsock/CMakeLists.txt | 16 + mbedsock/mbedsock.c | 188 +++++++++++ mbedsock/mbedsock.def | 4 + mbedsock/mbedsock.h | 109 +++++++ moxxmpp_socket/.flutter-plugins | 1 + moxxmpp_socket/.flutter-plugins-dependencies | 2 +- moxxmpp_socket/.gitignore | 44 +++ moxxmpp_socket/.metadata | 30 ++ moxxmpp_socket/analysis_options.yaml | 15 + .../integration_test/test_wrong_host.dart | 29 ++ moxxmpp_socket/lib/src/generated/ffi.dart | 293 ++++++++++++++++++ moxxmpp_socket/lib/src/ssl.dart | 86 +++++ moxxmpp_socket/pubspec.yaml | 13 + moxxmpp_socket/test/widget_test.dart | 30 ++ packages/mbedsock.nix | 15 + 17 files changed, 911 insertions(+), 1 deletion(-) create mode 100644 mbedsock/CMakeLists.txt create mode 100644 mbedsock/mbedsock.c create mode 100644 mbedsock/mbedsock.def create mode 100644 mbedsock/mbedsock.h create mode 100644 moxxmpp_socket/.gitignore create mode 100644 moxxmpp_socket/.metadata create mode 100644 moxxmpp_socket/analysis_options.yaml create mode 100644 moxxmpp_socket/integration_test/test_wrong_host.dart create mode 100644 moxxmpp_socket/lib/src/generated/ffi.dart create mode 100644 moxxmpp_socket/lib/src/ssl.dart create mode 100644 moxxmpp_socket/test/widget_test.dart create mode 100644 packages/mbedsock.nix diff --git a/README.md b/README.md index bd8cc24..485cf79 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ This package contains the actual XMPP code that is platform-independent. allows the user to resolve SRV records and thus support XEP-0368. Due to how DNS resolution is implemented, Flutter is required. +### mbedsock + +This package contains a C library that wraps [mbedTLS](https://github.com/Mbed-TLS/mbedtls) +into a form that makes it easily digestable in Dart for use in `moxxmpp_socket`. + +This is so that we can work around various issues with Dart's `SecureSocket`, though +mostly just the issue with setting a SNI value different from the connecting host name. + ## License See `./LICENSE`. diff --git a/flake.nix b/flake.nix index 3add579..c5ee892 100644 --- a/flake.nix +++ b/flake.nix @@ -30,14 +30,43 @@ useGoogleTVAddOns = false; }; pinnedJDK = pkgs.jdk; + + mbedsock = pkgs.callPackage ./packages/mbedsock.nix {}; in { + packages = { + inherit mbedsock; + }; + devShell = pkgs.mkShell { buildInputs = with pkgs; [ flutter pinnedJDK android.platform-tools dart # Flutter/Android gitlint # Code hygiene ripgrep # General utilities + + # Flutter dependencies for linux desktop + atk + cairo + clang + cmake + epoxy + gdk-pixbuf + glib + gtk3 + harfbuzz + ninja + pango + pcre + pkg-config + xorg.libX11 + xorg.xorgproto + + # moxxmpp_socket + mbedtls ]; + CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include"; + LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango mbedtls mbedsock ]; + ANDROID_HOME = (toString ./.) + "/.android/sdk"; JAVA_HOME = pinnedJDK; ANDROID_AVD_HOME = (toString ./.) + "/.android/avd"; diff --git a/mbedsock/CMakeLists.txt b/mbedsock/CMakeLists.txt new file mode 100644 index 0000000..eb6c9e9 --- /dev/null +++ b/mbedsock/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.7 FATAL_ERROR) +project(mbedsock VERSION 1.0.0 LANGUAGES C) +add_library(mbedsock SHARED mbedsock.c mbedsock.h mbedsock.def) + +target_include_directories(mbedsock PUBLIC ${MBEDTLS_ROOT_DIR}/include) +target_link_libraries(mbedsock mbedtls mbedx509 mbedcrypto) +link_directories(${MBEDTLS_ROOT_DIR}/lib) + +set_target_properties(mbedsock PROPERTIES + PUBLIC_HEADER mbedsock.h + VERSION ${PROJECT_VERSION} + SOVERSION 1 + OUTPUT_NAME "mbedsock" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Hex_Identity_ID_Goes_Here" +) +install(TARGETS mbedsock) diff --git a/mbedsock/mbedsock.c b/mbedsock/mbedsock.c new file mode 100644 index 0000000..e1a11bc --- /dev/null +++ b/mbedsock/mbedsock.c @@ -0,0 +1,188 @@ +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedsock.h" + +#include +#include +#include + +struct mbedsock_ctx *mbedsock_ctx_new_ex(const char *capath) { + struct mbedsock_ctx *ctx = malloc(sizeof(struct mbedsock_ctx)); + mbedsock_ctx_new(ctx, capath); + + return ctx; +} + +struct mbedsock *mbedsock_new_ex(struct mbedsock_ctx *ctx) { + struct mbedsock *sock = malloc(sizeof(struct mbedsock)); + mbedsock_new(ctx, sock); + + return sock; +} + +int mbedsock_ctx_new(struct mbedsock_ctx *ctx, const char *capath) { + int ret = 1; + + mbedtls_x509_crt_init(&ctx->chain); + mbedtls_ctr_drbg_init(&ctx->ctr_drbg); + mbedtls_entropy_init(&ctx->entropy); + if ((ret = mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy, + (unsigned char *) SSL_PERS, + SSL_PERS_LEN)) != 0) + return ret; + + if((ret = mbedtls_x509_crt_parse_path(&ctx->chain, capath)) < 0 ) + return ret; + + return 0; +} + +int mbedsock_new(struct mbedsock_ctx *ctx, struct mbedsock *sock) { + int ret = 1; + mbedtls_net_init(&sock->server_fd); + mbedtls_ssl_init(&sock->ssl); + mbedtls_ssl_config_init(&sock->conf); + + mbedtls_ssl_conf_authmode(&sock->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_ca_chain(&sock->conf, &ctx->chain, NULL); + mbedtls_ssl_conf_rng(&sock->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg); + + if ((ret = mbedtls_ssl_setup(&sock->ssl, &sock->conf)) != 0) + return ret; + + mbedtls_ssl_set_bio(&sock->ssl, &sock->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + if ((ret = mbedtls_ssl_config_defaults(&sock->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) + return ret; + + sock->secure = false; + + return 0; +} + +void mbedsock_free(struct mbedsock *sock) { + mbedtls_net_free(&sock->server_fd); + mbedtls_ssl_free(&sock->ssl); + mbedtls_ssl_config_free(&sock->conf); +} + +void mbedsock_ctx_free(struct mbedsock_ctx *ctx) { + mbedtls_x509_crt_free(&ctx->chain); + mbedtls_ctr_drbg_free(&ctx->ctr_drbg); + mbedtls_entropy_free(&ctx->entropy); +} + +void mbedsock_free_ex(struct mbedsock *sock) { + mbedsock_free(sock); + free(sock); +} + +void mbedsock_ctx_free_ex(struct mbedsock_ctx *ctx) { + mbedsock_ctx_free(ctx); + free(ctx); +} + +int mbedsock_do_handshake(struct mbedsock *sock, const char *alpn, const char *sni) { + int ret = 1; + + // Set ALPN, if desired + if (alpn != NULL) { + const char *alpn_list[2]; + alpn_list[0] = alpn; + alpn_list[1] = NULL; + + if ((ret = mbedtls_ssl_conf_alpn_protocols(&sock->conf, alpn_list)) != 0) { + return ret; + } + } + + // Set SNI, if desired + if (sni != NULL) { + if ((ret = mbedtls_ssl_set_hostname(&sock->ssl, sni)) != 0) { + return ret; + } + } + + while ((ret = mbedtls_ssl_handshake(&sock->ssl)) != 0) { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + return ret; + } + + // Verify the certificates + if ((ret = mbedtls_ssl_get_verify_result(&sock->ssl)) != 0) { + return ret; + } + + sock->secure = true; + return 0; +} + +int mbedsock_connect_secure(struct mbedsock *sock, const char *host, const char *port, const char *alpn, const char *sni) { + int ret = 1; + + if ((ret = mbedtls_net_connect(&sock->server_fd, host, port, MBEDTLS_NET_PROTO_TCP)) != 0) + return ret; + + if ((ret = mbedsock_do_handshake(sock, alpn, sni))) + return ret; + + return 0; +} + +int mbedsock_connect(struct mbedsock *sock, const char *host, const char *port) { + return mbedtls_net_connect(&sock->server_fd, host, port, MBEDTLS_NET_PROTO_TCP); +} + +int mbedsock_write(struct mbedsock *sock, const unsigned char *data, int len) { + int ret = 1; + + if (sock->secure) { + while ((ret = mbedtls_ssl_write(&sock->ssl, data, len)) <= 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + return -1; + } + } else { + if ((ret = mbedtls_net_send(&sock->server_fd, data, len)) <= 0) + return -1; + } + + return ret; +} + +int mbedsock_read(struct mbedsock *sock, unsigned char *buf, int len) { + int ret = 1; + + memset(buf, 0, len); + if (sock->secure) { + do { + ret = mbedtls_ssl_read(&sock->ssl, buf, len); + + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) + continue; + + // TODO: Notify + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + break; + + if (ret < 0) + return -1; + + return ret; + } while (true); + } else { + ret = mbedtls_net_recv(&sock->server_fd, buf, len); + + if (ret < 0) + return -1; + + return ret; + } + + return 0; +} + +bool mbedsock_is_secure(struct mbedsock *sock) { + return sock->secure; +} diff --git a/mbedsock/mbedsock.def b/mbedsock/mbedsock.def new file mode 100644 index 0000000..ac29e61 --- /dev/null +++ b/mbedsock/mbedsock.def @@ -0,0 +1,4 @@ +LIBRARY mbedsock +EXPORTS + mbedsock_ctx_new + mbedsock_new \ No newline at end of file diff --git a/mbedsock/mbedsock.h b/mbedsock/mbedsock.h new file mode 100644 index 0000000..1aa9eee --- /dev/null +++ b/mbedsock/mbedsock.h @@ -0,0 +1,109 @@ +#ifndef __MBEDSOCK_H__ +#define __MBEDSOCK_H__ + +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ctr_drbg.h" + +#include + +#define SSL_PERS "moxxmpp_socket" +#define SSL_PERS_LEN sizeof(SSL_PERS)/sizeof(char) + +/* + * The context for the sockets. This must be created once and is shared between all + * sockets. + */ +struct mbedsock_ctx { + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_x509_crt chain; +}; + +/* + * The data for the socket. + */ +struct mbedsock { + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_net_context server_fd; + + // Indicates whether the socket is secured using TLS (true) or not (false). + bool secure; +}; + +/* + * Create a new mbedsock_ctx context and write it to @ctx. @capath is the path + * to the directory containing the system's .crt root CA files. + * + * Returns true if everything went well; something non-zero on errors. + */ +int mbedsock_ctx_new(struct mbedsock_ctx *ctx, const char *capath); +struct mbedsock_ctx *mbedsock_ctx_new_ex(const char *capath); + +/* + * Create a new socket using the context @ctx and writes it to @sock. Returns zero + * on success; something non-zero on error. + */ +int mbedsock_new(struct mbedsock_ctx *ctx, struct mbedsock *sock); +struct mbedsock *mbedsock_new_ex(struct mbedsock_ctx *ctx); + +/* + * Free the resources used by @sock. + */ +void mbedsock_free(struct mbedsock *sock); +void mbedsock_free_ex(struct mbedsock *sock); + +/* + * Free the resources used by @ctx. + */ +void mbedsock_ctx_free(struct mbedsock_ctx *ctx); +void mbedsock_ctx_free_ex(struct mbedsock_ctx *ctx); + +/* + * Performs the TLS handshake and upgrades the connection @sock to a secured one. + * If @alpn is not NULL, then its value will be used for TLS ALPN. If @sni is not NULL, + * then its value will be used for Server Name Indication. + * + * Returns 0 on success; something non-zero on failure. + */ +int mbedsock_do_handshake(struct mbedsock *sock, const char *alpn, const char *sni); + +/* + * Use socket @sock to to connect to @host:@port and immediately call + * mbedsock_do_handshake. @alpn and @sni are used for mbedsock_do_handshake. + * + * Returns 0 on success; something non-zero on failure. + */ +int mbedsock_connect_secure(struct mbedsock *sock, const char *host, const char *port, const char *alpn, const char *sni); + +/* + * Use socket @sock to to connect to @host:@port. The socket is not secured on success. + * + * Returns 0 on success; something non-zero on failure. + */ +int mbedsock_connect(struct mbedsock *sock, const char *host, const char *port); + +/* + * Write @data - @len being the amount of bytes in data to read - to @sock. The function + * uses @sock's secure attribute to decide whether to use TLS or not. + * + * Returns the amount of bytes written on success. The documentation for + * mbedtls_ssl_write and mbedtls_net_send apply for the return value. Returns -1 + * if an error occurred. + */ +int mbedsock_write(struct mbedsock *sock, const unsigned char *data, int len); + +/* + * Read data from @sock into @buf. @len is the size of the buffer. + * + * Returns the amount of bytes read on success. The documentation for + * mbedtls_ssl_read and mbedtls_net_recv apply for the return value. Returns -1 + * if an error occurred. + */ +int mbedsock_read(struct mbedsock *sock, unsigned char *buf, int len); + +bool mbedsock_is_secure(struct mbedsock *sock); + +#endif // __MBEDSOCK_H__ diff --git a/moxxmpp_socket/.flutter-plugins b/moxxmpp_socket/.flutter-plugins index 4822cf8..49795d9 100644 --- a/moxxmpp_socket/.flutter-plugins +++ b/moxxmpp_socket/.flutter-plugins @@ -1,4 +1,5 @@ # This is a generated file; do not edit or check into version control. +integration_test=/nix/store/8gcfk0g1lg8gccd9kv3rzj910w9pz1kj-flutter-3.3.3-unwrapped/packages/integration_test/ moxdns=/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns-0.1.4/ moxdns_android=/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_android-0.1.4/ moxdns_linux=/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_linux-0.1.4/ diff --git a/moxxmpp_socket/.flutter-plugins-dependencies b/moxxmpp_socket/.flutter-plugins-dependencies index f1159d7..22cea49 100644 --- a/moxxmpp_socket/.flutter-plugins-dependencies +++ b/moxxmpp_socket/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[],"android":[{"name":"moxdns_android","path":"/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_android-0.1.4/","native_build":true,"dependencies":[]}],"macos":[],"linux":[{"name":"moxdns_linux","path":"/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_linux-0.1.4/","native_build":true,"dependencies":[]}],"windows":[],"web":[]},"dependencyGraph":[{"name":"moxdns","dependencies":["moxdns_android","moxdns_linux"]},{"name":"moxdns_android","dependencies":["moxdns"]},{"name":"moxdns_linux","dependencies":["moxdns"]}],"date_created":"2022-11-05 14:01:31.714716","version":"3.3.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/nix/store/8gcfk0g1lg8gccd9kv3rzj910w9pz1kj-flutter-3.3.3-unwrapped/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/nix/store/8gcfk0g1lg8gccd9kv3rzj910w9pz1kj-flutter-3.3.3-unwrapped/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"moxdns_android","path":"/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_android-0.1.4/","native_build":true,"dependencies":[]}],"macos":[],"linux":[{"name":"moxdns_linux","path":"/home/alexander/.pub-cache/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxdns_linux-0.1.4/","native_build":true,"dependencies":[]}],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]},{"name":"moxdns","dependencies":["moxdns_android","moxdns_linux"]},{"name":"moxdns_android","dependencies":["moxdns"]},{"name":"moxdns_linux","dependencies":["moxdns"]}],"date_created":"2022-11-07 22:04:34.759562","version":"3.3.3"} \ No newline at end of file diff --git a/moxxmpp_socket/.gitignore b/moxxmpp_socket/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/moxxmpp_socket/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/moxxmpp_socket/.metadata b/moxxmpp_socket/.metadata new file mode 100644 index 0000000..be43045 --- /dev/null +++ b/moxxmpp_socket/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 18a827f3933c19f51862dde3fa472197683249d6 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 18a827f3933c19f51862dde3fa472197683249d6 + base_revision: 18a827f3933c19f51862dde3fa472197683249d6 + - platform: linux + create_revision: 18a827f3933c19f51862dde3fa472197683249d6 + base_revision: 18a827f3933c19f51862dde3fa472197683249d6 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/moxxmpp_socket/analysis_options.yaml b/moxxmpp_socket/analysis_options.yaml new file mode 100644 index 0000000..4aa9d84 --- /dev/null +++ b/moxxmpp_socket/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:very_good_analysis/analysis_options.yaml +linter: + rules: + public_member_api_docs: false + lines_longer_than_80_chars: false + use_setters_to_change_properties: false + avoid_positional_boolean_parameters: false + avoid_bool_literals_in_conditional_expressions: false + +analyzer: + exclude: + - "**/*.g.dart" + - "**/*.freezed.dart" + - "lib/src/generated/*.dart" + - "test/" diff --git a/moxxmpp_socket/integration_test/test_wrong_host.dart b/moxxmpp_socket/integration_test/test_wrong_host.dart new file mode 100644 index 0000000..2e3f153 --- /dev/null +++ b/moxxmpp_socket/integration_test/test_wrong_host.dart @@ -0,0 +1,29 @@ +import 'dart:io'; +import 'package:moxxmpp_socket/src/ssl.dart'; + +void main(List argv) async { + if (argv.length != 2) { + print('Usage: test_wrong_host.dart server-addr host-name'); + exit(1); + } + + final server = argv[0]; + final hostname = argv[1]; + final ctx = MbedSockCtx('/etc/ssl/certs/'); + final sock = MbedSock(ctx); + + print('Connecting...'); + final done = sock.connectSecure( + server, + '5223', + alpn: 'xmpp-client', + hostname: hostname, + ); + + print('Success? $done'); + print('Secure? ${sock.isSecure()}'); + + sock.free(); + ctx.free(); + print('OKAY'); +} diff --git a/moxxmpp_socket/lib/src/generated/ffi.dart b/moxxmpp_socket/lib/src/generated/ffi.dart new file mode 100644 index 0000000..81888e9 --- /dev/null +++ b/moxxmpp_socket/lib/src/generated/ffi.dart @@ -0,0 +1,293 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +class NativeLibrary { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeLibrary(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeLibrary.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + int mbedsock_ctx_new( + ffi.Pointer ctx, + ffi.Pointer capath, + ) { + return _mbedsock_ctx_new( + ctx, + capath, + ); + } + + late final _mbedsock_ctx_newPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('mbedsock_ctx_new'); + late final _mbedsock_ctx_new = _mbedsock_ctx_newPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer mbedsock_ctx_new_ex( + ffi.Pointer capath, + ) { + return _mbedsock_ctx_new_ex( + capath, + ); + } + + late final _mbedsock_ctx_new_exPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('mbedsock_ctx_new_ex'); + late final _mbedsock_ctx_new_ex = _mbedsock_ctx_new_exPtr + .asFunction Function(ffi.Pointer)>(); + + int mbedsock_new( + ffi.Pointer ctx, + ffi.Pointer sock, + ) { + return _mbedsock_new( + ctx, + sock, + ); + } + + late final _mbedsock_newPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('mbedsock_new'); + late final _mbedsock_new = _mbedsock_newPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer mbedsock_new_ex( + ffi.Pointer ctx, + ) { + return _mbedsock_new_ex( + ctx, + ); + } + + late final _mbedsock_new_exPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('mbedsock_new_ex'); + late final _mbedsock_new_ex = _mbedsock_new_exPtr + .asFunction Function(ffi.Pointer)>(); + + void mbedsock_free( + ffi.Pointer sock, + ) { + return _mbedsock_free( + sock, + ); + } + + late final _mbedsock_freePtr = + _lookup)>>( + 'mbedsock_free'); + late final _mbedsock_free = + _mbedsock_freePtr.asFunction)>(); + + void mbedsock_free_ex( + ffi.Pointer sock, + ) { + return _mbedsock_free_ex( + sock, + ); + } + + late final _mbedsock_free_exPtr = + _lookup)>>( + 'mbedsock_free_ex'); + late final _mbedsock_free_ex = + _mbedsock_free_exPtr.asFunction)>(); + + void mbedsock_ctx_free( + ffi.Pointer ctx, + ) { + return _mbedsock_ctx_free( + ctx, + ); + } + + late final _mbedsock_ctx_freePtr = + _lookup)>>( + 'mbedsock_ctx_free'); + late final _mbedsock_ctx_free = _mbedsock_ctx_freePtr + .asFunction)>(); + + void mbedsock_ctx_free_ex( + ffi.Pointer ctx, + ) { + return _mbedsock_ctx_free_ex( + ctx, + ); + } + + late final _mbedsock_ctx_free_exPtr = + _lookup)>>( + 'mbedsock_ctx_free_ex'); + late final _mbedsock_ctx_free_ex = _mbedsock_ctx_free_exPtr + .asFunction)>(); + + int mbedsock_do_handshake( + ffi.Pointer sock, + ffi.Pointer alpn, + ffi.Pointer sni, + ) { + return _mbedsock_do_handshake( + sock, + alpn, + sni, + ); + } + + late final _mbedsock_do_handshakePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('mbedsock_do_handshake'); + late final _mbedsock_do_handshake = _mbedsock_do_handshakePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + int mbedsock_connect_secure( + ffi.Pointer sock, + ffi.Pointer host, + ffi.Pointer port, + ffi.Pointer alpn, + ffi.Pointer sni, + ) { + return _mbedsock_connect_secure( + sock, + host, + port, + alpn, + sni, + ); + } + + late final _mbedsock_connect_securePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>('mbedsock_connect_secure'); + late final _mbedsock_connect_secure = _mbedsock_connect_securePtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + int mbedsock_connect( + ffi.Pointer sock, + ffi.Pointer host, + ffi.Pointer port, + ) { + return _mbedsock_connect( + sock, + host, + port, + ); + } + + late final _mbedsock_connectPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('mbedsock_connect'); + late final _mbedsock_connect = _mbedsock_connectPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + int mbedsock_write( + ffi.Pointer sock, + ffi.Pointer data, + int len, + ) { + return _mbedsock_write( + sock, + data, + len, + ); + } + + late final _mbedsock_writePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Int32)>>('mbedsock_write'); + late final _mbedsock_write = _mbedsock_writePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, int)>(); + + int mbedsock_read( + ffi.Pointer sock, + ffi.Pointer buf, + int len, + ) { + return _mbedsock_read( + sock, + buf, + len, + ); + } + + late final _mbedsock_readPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Int32)>>('mbedsock_read'); + late final _mbedsock_read = _mbedsock_readPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, int)>(); + + int mbedsock_is_secure( + ffi.Pointer sock, + ) { + return _mbedsock_is_secure( + sock, + ); + } + + late final _mbedsock_is_securePtr = + _lookup)>>( + 'mbedsock_is_secure'); + late final _mbedsock_is_secure = + _mbedsock_is_securePtr.asFunction)>(); +} + +class mbedsock_ctx extends ffi.Struct { + @ffi.Int32() + external int entropy; + + @ffi.Int32() + external int ctr_drbg; + + @ffi.Int32() + external int chain; +} + +class mbedsock extends ffi.Struct { + @ffi.Int32() + external int ssl; + + @ffi.Int32() + external int conf; + + @ffi.Int32() + external int server_fd; + + @ffi.Int32() + external int secure; +} + +const String SSL_PERS = 'moxxmpp_socket'; + +const int SSL_PERS_LEN = 15; diff --git a/moxxmpp_socket/lib/src/ssl.dart b/moxxmpp_socket/lib/src/ssl.dart new file mode 100644 index 0000000..6ae5e7e --- /dev/null +++ b/moxxmpp_socket/lib/src/ssl.dart @@ -0,0 +1,86 @@ +import 'dart:io'; +import 'dart:ffi'; +import 'dart:typed_data'; +import 'package:ffi/ffi.dart'; +import 'package:path/path.dart' as path; +import 'package:moxxmpp_socket/src/generated/ffi.dart' as libmbedsock; + +//final libPath = path.join(Directory.current.path, 'libmbedsock.so'); +final lib = libmbedsock.NativeLibrary(DynamicLibrary.open('libmbedsock.so')); + +class MbedSockCtx { + late Pointer _ctxPtr; + + MbedSockCtx(String caPath) { + final caPathNative = caPath.toNativeUtf8(); + _ctxPtr = lib.mbedsock_ctx_new_ex(caPathNative.cast()); + malloc.free(caPathNative); + } + + void free() { + lib.mbedsock_ctx_free_ex(_ctxPtr); + } + + Pointer get ctx => _ctxPtr; +} + +class MbedSock { + late Pointer sock; + + MbedSock(MbedSockCtx ctx) { + sock = lib.mbedsock_new_ex(ctx.ctx); + } + + bool connect(String host, int port) { + final nativeHost = host.toNativeUtf8(); + final nativePort = port.toString().toNativeUtf8(); + final ret = lib.mbedsock_connect(sock, nativeHost.cast(), nativePort.cast()); + + malloc + ..free(nativeHost) + ..free(nativePort); + + return ret == 0; + } + + bool connectSecure(String host, String port, {String? alpn, String? hostname}) { + final nativeHost = host.toNativeUtf8(); + final nativePort = port.toNativeUtf8(); + final nativeAlpn = alpn != null ? alpn.toNativeUtf8() : nullptr; + final nativeHostname = hostname != null ? hostname.toNativeUtf8() : nullptr; + + final ret = lib.mbedsock_connect_secure( + sock, + nativeHost.cast(), + nativePort.cast(), + nativeAlpn.cast(), + nativeHostname.cast(), + ); + + malloc + ..free(nativeHost) + ..free(nativePort); + + if (alpn != null) { + malloc.free(nativeAlpn); + } + if (hostname != null) { + malloc.free(nativeHostname); + } + + print(ret); + return ret == 0; + } + + bool isSecure() { + return lib.mbedsock_is_secure(sock) == 1; + } + + void write(String data) { + //lib.mbedsock_write(sock, data, data.length); + } + + void free() { + lib.mbedsock_free_ex(sock); + } +} diff --git a/moxxmpp_socket/pubspec.yaml b/moxxmpp_socket/pubspec.yaml index 68ee641..622c9d2 100644 --- a/moxxmpp_socket/pubspec.yaml +++ b/moxxmpp_socket/pubspec.yaml @@ -9,6 +9,7 @@ environment: flutter: '>=2.13.0-0.1' dependencies: + ffi: logging: 1.0.2 moxdns: hosted: https://git.polynom.me/api/packages/Moxxy/pub @@ -16,8 +17,20 @@ dependencies: moxxmpp: hosted: https://git.polynom.me/api/packages/Moxxy/pub version: 0.1.0 + path: 1.8.2 dev_dependencies: + integration_test: + sdk: flutter + ffigen: ^4.1.2 lints: ^2.0.0 test: ^1.16.0 very_good_analysis: ^3.0.1 + +ffigen: + output: 'lib/src/generated/ffi.dart' + llvm-path: + - '/nix/store/zq6966rjlmmwjym4jlnymjwwgjhcgryz-clang-11.1.0-lib' + headers: + entry-points: + - ../mbedsock/mbedsock.h diff --git a/moxxmpp_socket/test/widget_test.dart b/moxxmpp_socket/test/widget_test.dart new file mode 100644 index 0000000..9961d01 --- /dev/null +++ b/moxxmpp_socket/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:moxxmpp_socket/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/packages/mbedsock.nix b/packages/mbedsock.nix new file mode 100644 index 0000000..3d12bd9 --- /dev/null +++ b/packages/mbedsock.nix @@ -0,0 +1,15 @@ +{ + stdenv, cmake, pkg-config +, mbedtls +}: + +stdenv.mkDerivation { + pname = "mbedsock"; + version = "0.1.1"; + + src = ./mbedsock; + + cmakeFlags = [ "-DMBEDTLS_ROOT_DIR=${mbedtls}" ]; + + buildInputs = [ cmake mbedtls pkg-config ]; +}