diff --git a/example/linux/.gitignore b/example/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt new file mode 100644 index 0000000..428fd4b --- /dev/null +++ b/example/linux/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "argon2_ffi_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "\$ORIGIN") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "window_configuration.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# BundleUtilities doesn't re-copy changed files, so start with a clean build +# bundle directory every time. +INSTALL(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +file(GLOB FLUTTER_PLUGIN_DIRS "${PROJECT_BINARY_DIR}/plugins/*") + +INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${BINARY_NAME}\" \"\" + \"${FLUTTER_PLUGIN_DIRS};${FLUTTER_LIBRARY_DIR}\") + " COMPONENT Runtime) + +INSTALL(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +INSTALL(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +INSTALL(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the lib directory on non-Debug builds only. +if (NOT CMAKE_BUILD_TYPE MATCHES "Debug") + INSTALL(DIRECTORY "${PROJECT_BUILD_DIR}/lib" + DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT Runtime) +endif() diff --git a/example/linux/flutter/.template_version b/example/linux/flutter/.template_version new file mode 100644 index 0000000..e440e5c --- /dev/null +++ b/example/linux/flutter/.template_version @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..902cfa4 --- /dev/null +++ b/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper_glfw") + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_glfw.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY_DIR "${EPHEMERAL_DIR}" PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_glfw.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" +) +list_prepend(FLUTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "engine_method_result.cc" + "standard_codec.cc" +) +list_prepend(CPP_WRAPPER_SOURCES_CORE "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list_prepend(CPP_WRAPPER_SOURCES_PLUGIN "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_window_controller.cc" +) +list_prepend(CPP_WRAPPER_SOURCES_APP "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..2a4f15e --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + Argon2FfiPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("Argon2FfiPlugin")); +} diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..9846246 --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..8cf3ed7 --- /dev/null +++ b/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,8 @@ +list(APPEND FLUTTER_PLUGIN_LIST + argon2_ffi +) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) +endforeach(plugin) diff --git a/example/linux/main.cc b/example/linux/main.cc new file mode 100644 index 0000000..5211469 --- /dev/null +++ b/example/linux/main.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "flutter/generated_plugin_registrant.h" +#include "window_configuration.h" + +namespace { + +// Runs the application in headless mode, without a window. +void RunHeadless(const std::string& icu_data_path, + const std::string& assets_path, + const std::vector& arguments, + const std::string& aot_library_path) { + flutter::FlutterEngine engine; + engine.Start(icu_data_path, assets_path, arguments, aot_library_path); + RegisterPlugins(&engine); + while (true) { + engine.RunEventLoopWithTimeout(); + } +} + +} // namespace + +int main(int argc, char **argv) { + std::string data_directory = "data"; + std::string assets_path = data_directory + "/flutter_assets"; + std::string icu_data_path = data_directory + "/icudtl.dat"; + + std::string lib_directory = "lib"; + std::string aot_library_path = lib_directory + "/libapp.so"; + + // Arguments for the Flutter Engine. + std::vector arguments; + + flutter::FlutterWindowController flutter_controller(icu_data_path); + flutter::WindowProperties window_properties = {}; + window_properties.title = kFlutterWindowTitle; + window_properties.width = kFlutterWindowWidth; + window_properties.height = kFlutterWindowHeight; + + // Start the engine. + if (!flutter_controller.CreateWindow(window_properties, assets_path, + arguments, aot_library_path)) { + if (getenv("DISPLAY") == nullptr) { + std::cout << "No DISPLAY; falling back to headless mode." << std::endl; + RunHeadless(icu_data_path, assets_path, arguments, aot_library_path); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; + } + RegisterPlugins(&flutter_controller); + + // Run until the window is closed. + while (flutter_controller.RunEventLoopWithTimeout()) { + } + return EXIT_SUCCESS; +} diff --git a/example/linux/window_configuration.cc b/example/linux/window_configuration.cc new file mode 100644 index 0000000..cd5dfa3 --- /dev/null +++ b/example/linux/window_configuration.cc @@ -0,0 +1,5 @@ +#include "window_configuration.h" + +const char *kFlutterWindowTitle = "argon2_ffi_example"; +const unsigned int kFlutterWindowWidth = 1280; +const unsigned int kFlutterWindowHeight = 720; diff --git a/example/linux/window_configuration.h b/example/linux/window_configuration.h new file mode 100644 index 0000000..5de7011 --- /dev/null +++ b/example/linux/window_configuration.h @@ -0,0 +1,15 @@ +#ifndef WINDOW_CONFIGURATION_ +#define WINDOW_CONFIGURATION_ + +// This is a temporary approach to isolate common customizations from main.cpp, +// where the APIs are still in flux. This should simplify re-creating the +// runner while preserving local changes. +// +// Longer term there should be simpler configuration options for common +// customizations like this, without requiring native code changes. + +extern const char *kFlutterWindowTitle; +extern const unsigned int kFlutterWindowWidth; +extern const unsigned int kFlutterWindowHeight; + +#endif // WINDOW_CONFIGURATION_ diff --git a/ios/Classes/argon2_ffi.c b/ios/Classes/argon2_ffi.c index 4f7f8bd..8081c42 100644 --- a/ios/Classes/argon2_ffi.c +++ b/ios/Classes/argon2_ffi.c @@ -107,7 +107,7 @@ char *hp_argon2_hash(uint8_t *key, uint32_t keylen, uint8_t *salt, uint32_t salt uint8_t type, int32_t version) { uint8_t hash1[hashlen]; PRINT_DEBUG("keylen: %ld, saltlen: %ld, m_cost: %ld, t_cost: %ld, parallelism: %ld, hashlen: %ld, type: %d, version: %02x\n", - keylen, saltlen, m_cost, t_cost, parallelism, hashlen, type, version); + (long)keylen, (long)saltlen, (long)m_cost, (long)t_cost, (long)parallelism, (long)hashlen, type, version); PRINT_DEBUG("key: "); debugBytes(key, keylen); diff --git a/lib/argon2_ffi.dart b/lib/argon2_ffi.dart index 50d0fc3..d66ce89 100644 --- a/lib/argon2_ffi.dart +++ b/lib/argon2_ffi.dart @@ -5,23 +5,48 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart'; -typedef HashStuff = Pointer Function(Pointer str); +typedef Argon2HashNative = Pointer Function( + Pointer key, + Uint32 keyLen, + Pointer salt, + Uint32 saltlen, + Uint32 m_cost, // memory cost + Uint32 t_cost, // time cost (number iterations) + Uint32 parallelism, + IntPtr hashlen, + Uint8 type, + Uint32 version, +); -class Argon2Ffi { - static const MethodChannel _channel = const MethodChannel('argon2_ffi'); +typedef Argon2Hash = Pointer Function( + Pointer key, + int keyLen, + Pointer salt, + int saltlen, + int m_cost, // memory cost + int t_cost, // time cost (number iterations) + int parallelism, + int hashlen, + int type, + int version, +); +class Argon2Ffi { int Function(int x, int y) _nativeAdd; - HashStuff _hashStuff; + Argon2Hash argon2hash; Argon2Ffi() { final argon2lib = Platform.isAndroid ? DynamicLibrary.open('libargon2_ffi.so') - : DynamicLibrary.executable(); + : Platform.isLinux + ? DynamicLibrary.open('libargon2_ffi_plugin.so') + : DynamicLibrary.executable(); _nativeAdd = argon2lib .lookup>('native_add') .asFunction(); - _hashStuff = - argon2lib.lookup>('hashStuff').asFunction(); + argon2hash = argon2lib + .lookup>('hp_argon2_hash') + .asFunction(); } int addIt(int x, int y) => _nativeAdd(x, y); diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 57abc29..c330cd7 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.10) set(PROJECT_NAME "argon2_ffi") -project(${PROJECT_NAME} LANGUAGES CXX) +project(${PROJECT_NAME} LANGUAGES C CXX) set(PLUGIN_NAME "${PROJECT_NAME}_plugin") add_library(${PLUGIN_NAME} SHARED - "${PLUGIN_NAME}.cc" + ${PLUGIN_NAME}.cc ../ios/Classes/argon2_ffi.c