Compare commits
9 Commits
8ca121b910
...
9f94c6da97
| Author | SHA1 | Date |
|---|---|---|
|
|
9f94c6da97 | |
|
|
a5728b15f9 | |
|
|
143427a5d5 | |
|
|
37d53398ef | |
|
|
33a4296374 | |
|
|
37b5ca6e8d | |
|
|
b79878a0b5 | |
|
|
a129711e4d | |
|
|
9d8c2594d8 |
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"chat.tools.terminal.autoApprove": {
|
||||||
|
"cargo check": true,
|
||||||
|
"flutter_rust_bridge_codegen": true,
|
||||||
|
"rustup": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ This repository contains:
|
||||||
|
|
||||||
* A small Rust bridge crate referencing ``ccc_rust`` via a git tag
|
* A small Rust bridge crate referencing ``ccc_rust`` via a git tag
|
||||||
* Generated ``flutter_rust_bridge`` bindings
|
* Generated ``flutter_rust_bridge`` bindings
|
||||||
* A Flutter plugin scaffold (iOS / Android / macOS)
|
* A Flutter plugin scaffold (iOS / Android / macOS / Linux / Windows)
|
||||||
* A clean Dart API surface for applications
|
* A clean Dart API surface for applications
|
||||||
|
|
||||||
This plugin does **not** implement cryptography itself.
|
This plugin does **not** implement cryptography itself.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// The Android Gradle Plugin builds the native code with the Android NDK.
|
// The Android Gradle Plugin builds the native code with the Android NDK.
|
||||||
|
|
||||||
group = "com.example.ccc_cryptography"
|
group 'com.flutter_rust_bridge.ccc_cryptography'
|
||||||
version = "1.0"
|
version '1.0'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
|
@ -11,7 +11,7 @@ buildscript {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// The Android Gradle Plugin knows how to build native code with the NDK.
|
// The Android Gradle Plugin knows how to build native code with the NDK.
|
||||||
classpath("com.android.tools.build:gradle:8.11.1")
|
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,42 +22,35 @@ rootProject.allprojects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: "com.android.library"
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.example.ccc_cryptography"
|
if (project.android.hasProperty("namespace")) {
|
||||||
|
namespace 'com.flutter_rust_bridge.ccc_cryptography'
|
||||||
|
}
|
||||||
|
|
||||||
// Bumping the plugin compileSdk version requires all clients of this plugin
|
// Bumping the plugin compileSdkVersion requires all clients of this plugin
|
||||||
// to bump the version in their app.
|
// to bump the version in their app.
|
||||||
compileSdk = 36
|
compileSdkVersion 33
|
||||||
|
|
||||||
// Use the NDK version
|
// Use the NDK version
|
||||||
// declared in /android/app/build.gradle file of the Flutter project.
|
// declared in /android/app/build.gradle file of the Flutter project.
|
||||||
// Replace it with a version number if this plugin requires a specific NDK version.
|
// Replace it with a version number if this plugin requires a specfic NDK version.
|
||||||
// (e.g. ndkVersion "23.1.7779620")
|
// (e.g. ndkVersion "23.1.7779620")
|
||||||
ndkVersion = android.ndkVersion
|
ndkVersion android.ndkVersion
|
||||||
|
|
||||||
// Invoke the shared CMake build with the Android Gradle Plugin.
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
path = "../src/CMakeLists.txt"
|
|
||||||
|
|
||||||
// The default CMake version for the Android Gradle Plugin is 3.10.2.
|
|
||||||
// https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
|
|
||||||
//
|
|
||||||
// The Flutter tooling requires that developers have CMake 3.10 or later
|
|
||||||
// installed. You should not increase this version, as doing so will cause
|
|
||||||
// the plugin to fail to compile for some customers of the plugin.
|
|
||||||
// version "3.10.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 24
|
minSdkVersion 19
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply from: "../cargokit/gradle/plugin.gradle"
|
||||||
|
cargokit {
|
||||||
|
manifestDir = "../rust"
|
||||||
|
libname = "ccc_cryptography"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.example.ccc_cryptography">
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
target
|
||||||
|
.dart_tool
|
||||||
|
*.iml
|
||||||
|
!pubspec.lock
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
Copyright 2022 Matej Knopp
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
MIT LICENSE
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
APACHE LICENSE, VERSION 2.0
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
Experimental repository to provide glue for seamlessly integrating cargo build
|
||||||
|
with flutter plugins and packages.
|
||||||
|
|
||||||
|
See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/
|
||||||
|
for a tutorial on how to use Cargokit.
|
||||||
|
|
||||||
|
Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASEDIR=$(dirname "$0")
|
||||||
|
|
||||||
|
# Workaround for https://github.com/dart-lang/pub/issues/4010
|
||||||
|
BASEDIR=$(cd "$BASEDIR" ; pwd -P)
|
||||||
|
|
||||||
|
# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project
|
||||||
|
NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"`
|
||||||
|
|
||||||
|
export PATH=${NEW_PATH%?} # remove trailing :
|
||||||
|
|
||||||
|
env
|
||||||
|
|
||||||
|
# Platform name (macosx, iphoneos, iphonesimulator)
|
||||||
|
export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME
|
||||||
|
|
||||||
|
# Arctive architectures (arm64, armv7, x86_64), space separated.
|
||||||
|
export CARGOKIT_DARWIN_ARCHS=$ARCHS
|
||||||
|
|
||||||
|
# Current build configuration (Debug, Release)
|
||||||
|
export CARGOKIT_CONFIGURATION=$CONFIGURATION
|
||||||
|
|
||||||
|
# Path to directory containing Cargo.toml.
|
||||||
|
export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1
|
||||||
|
|
||||||
|
# Temporary directory for build artifacts.
|
||||||
|
export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR
|
||||||
|
|
||||||
|
# Output directory for final artifacts.
|
||||||
|
export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME
|
||||||
|
|
||||||
|
# Directory to store built tool artifacts.
|
||||||
|
export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool
|
||||||
|
|
||||||
|
# Directory inside root project. Not necessarily the top level directory of root project.
|
||||||
|
export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT
|
||||||
|
|
||||||
|
FLUTTER_EXPORT_BUILD_ENVIRONMENT=(
|
||||||
|
"$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS
|
||||||
|
"$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS
|
||||||
|
)
|
||||||
|
|
||||||
|
for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}"
|
||||||
|
do
|
||||||
|
if [[ -f "$path" ]]; then
|
||||||
|
source "$path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
sh "$BASEDIR/run_build_tool.sh" build-pod "$@"
|
||||||
|
|
||||||
|
# Make a symlink from built framework to phony file, which will be used as input to
|
||||||
|
# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate
|
||||||
|
# attribute on custom build phase)
|
||||||
|
ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony"
|
||||||
|
ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out"
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
A sample command-line application with an entrypoint in `bin/`, library code
|
||||||
|
in `lib/`, and example unit test in `test/`.
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
- prefer_relative_imports
|
||||||
|
- directives_ordering
|
||||||
|
|
||||||
|
# analyzer:
|
||||||
|
# exclude:
|
||||||
|
# - path/to/excluded/files/**
|
||||||
|
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'package:build_tool/build_tool.dart' as build_tool;
|
||||||
|
|
||||||
|
void main(List<String> arguments) {
|
||||||
|
build_tool.runMain(arguments);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'src/build_tool.dart' as build_tool;
|
||||||
|
|
||||||
|
Future<void> runMain(List<String> args) async {
|
||||||
|
return build_tool.runMain(args);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:version/version.dart';
|
||||||
|
|
||||||
|
import 'target.dart';
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
class AndroidEnvironment {
|
||||||
|
AndroidEnvironment({
|
||||||
|
required this.sdkPath,
|
||||||
|
required this.ndkVersion,
|
||||||
|
required this.minSdkVersion,
|
||||||
|
required this.targetTempDir,
|
||||||
|
required this.target,
|
||||||
|
});
|
||||||
|
|
||||||
|
static void clangLinkerWrapper(List<String> args) {
|
||||||
|
final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG'];
|
||||||
|
if (clang == null) {
|
||||||
|
throw Exception(
|
||||||
|
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var");
|
||||||
|
}
|
||||||
|
final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET'];
|
||||||
|
if (target == null) {
|
||||||
|
throw Exception(
|
||||||
|
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var");
|
||||||
|
}
|
||||||
|
|
||||||
|
runCommand(clang, [
|
||||||
|
target,
|
||||||
|
...args,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Full path to Android SDK.
|
||||||
|
final String sdkPath;
|
||||||
|
|
||||||
|
/// Full version of Android NDK.
|
||||||
|
final String ndkVersion;
|
||||||
|
|
||||||
|
/// Minimum supported SDK version.
|
||||||
|
final int minSdkVersion;
|
||||||
|
|
||||||
|
/// Target directory for build artifacts.
|
||||||
|
final String targetTempDir;
|
||||||
|
|
||||||
|
/// Target being built.
|
||||||
|
final Target target;
|
||||||
|
|
||||||
|
bool ndkIsInstalled() {
|
||||||
|
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
|
||||||
|
final ndkPackageXml = File(path.join(ndkPath, 'package.xml'));
|
||||||
|
return ndkPackageXml.existsSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void installNdk({
|
||||||
|
required String javaHome,
|
||||||
|
}) {
|
||||||
|
final sdkManagerExtension = Platform.isWindows ? '.bat' : '';
|
||||||
|
final sdkManager = path.join(
|
||||||
|
sdkPath,
|
||||||
|
'cmdline-tools',
|
||||||
|
'latest',
|
||||||
|
'bin',
|
||||||
|
'sdkmanager$sdkManagerExtension',
|
||||||
|
);
|
||||||
|
|
||||||
|
log.info('Installing NDK $ndkVersion');
|
||||||
|
runCommand(sdkManager, [
|
||||||
|
'--install',
|
||||||
|
'ndk;$ndkVersion',
|
||||||
|
], environment: {
|
||||||
|
'JAVA_HOME': javaHome,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> buildEnvironment() async {
|
||||||
|
final hostArch = Platform.isMacOS
|
||||||
|
? "darwin-x86_64"
|
||||||
|
: (Platform.isLinux ? "linux-x86_64" : "windows-x86_64");
|
||||||
|
|
||||||
|
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
|
||||||
|
final toolchainPath = path.join(
|
||||||
|
ndkPath,
|
||||||
|
'toolchains',
|
||||||
|
'llvm',
|
||||||
|
'prebuilt',
|
||||||
|
hostArch,
|
||||||
|
'bin',
|
||||||
|
);
|
||||||
|
|
||||||
|
final minSdkVersion =
|
||||||
|
math.max(target.androidMinSdkVersion!, this.minSdkVersion);
|
||||||
|
|
||||||
|
final exe = Platform.isWindows ? '.exe' : '';
|
||||||
|
|
||||||
|
final arKey = 'AR_${target.rust}';
|
||||||
|
final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe']
|
||||||
|
.map((e) => path.join(toolchainPath, e))
|
||||||
|
.firstWhereOrNull((element) => File(element).existsSync());
|
||||||
|
if (arValue == null) {
|
||||||
|
throw Exception('Failed to find ar for $target in $toolchainPath');
|
||||||
|
}
|
||||||
|
|
||||||
|
final targetArg = '--target=${target.rust}$minSdkVersion';
|
||||||
|
|
||||||
|
final ccKey = 'CC_${target.rust}';
|
||||||
|
final ccValue = path.join(toolchainPath, 'clang$exe');
|
||||||
|
final cfFlagsKey = 'CFLAGS_${target.rust}';
|
||||||
|
final cFlagsValue = targetArg;
|
||||||
|
|
||||||
|
final cxxKey = 'CXX_${target.rust}';
|
||||||
|
final cxxValue = path.join(toolchainPath, 'clang++$exe');
|
||||||
|
final cxxFlagsKey = 'CXXFLAGS_${target.rust}';
|
||||||
|
final cxxFlagsValue = targetArg;
|
||||||
|
|
||||||
|
final linkerKey =
|
||||||
|
'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase();
|
||||||
|
|
||||||
|
final ranlibKey = 'RANLIB_${target.rust}';
|
||||||
|
final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe');
|
||||||
|
|
||||||
|
// Tell bindgen (libclang) where to find Android system headers like
|
||||||
|
// <pthread.h>. Without this, cross-compilation of crates that use
|
||||||
|
// bindgen will fail with "fatal error: 'pthread.h' file not found".
|
||||||
|
final sysroot = path.join(
|
||||||
|
ndkPath,
|
||||||
|
'toolchains',
|
||||||
|
'llvm',
|
||||||
|
'prebuilt',
|
||||||
|
hostArch,
|
||||||
|
'sysroot',
|
||||||
|
);
|
||||||
|
final bindgenKey =
|
||||||
|
'BINDGEN_EXTRA_CLANG_ARGS_${target.rust.replaceAll('-', '_')}';
|
||||||
|
final bindgenValue = '--sysroot=$sysroot $targetArg';
|
||||||
|
|
||||||
|
final ndkVersionParsed = Version.parse(ndkVersion);
|
||||||
|
final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS';
|
||||||
|
final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed);
|
||||||
|
|
||||||
|
final runRustTool =
|
||||||
|
Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh';
|
||||||
|
|
||||||
|
final packagePath = (await Isolate.resolvePackageUri(
|
||||||
|
Uri.parse('package:build_tool/buildtool.dart')))!
|
||||||
|
.toFilePath();
|
||||||
|
final selfPath = path.canonicalize(path.join(
|
||||||
|
packagePath,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
runRustTool,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Make sure that run_build_tool is working properly even initially launched directly
|
||||||
|
// through dart run.
|
||||||
|
final toolTempDir =
|
||||||
|
Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir;
|
||||||
|
|
||||||
|
return {
|
||||||
|
arKey: arValue,
|
||||||
|
ccKey: ccValue,
|
||||||
|
cfFlagsKey: cFlagsValue,
|
||||||
|
cxxKey: cxxValue,
|
||||||
|
cxxFlagsKey: cxxFlagsValue,
|
||||||
|
ranlibKey: ranlibValue,
|
||||||
|
rustFlagsKey: rustFlagsValue,
|
||||||
|
linkerKey: selfPath,
|
||||||
|
bindgenKey: bindgenValue,
|
||||||
|
// Recognized by main() so we know when we're acting as a wrapper
|
||||||
|
'_CARGOKIT_NDK_LINK_TARGET': targetArg,
|
||||||
|
'_CARGOKIT_NDK_LINK_CLANG': ccValue,
|
||||||
|
'CARGOKIT_TOOL_TEMP_DIR': toolTempDir,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for libgcc missing in NDK23, inspired by cargo-ndk
|
||||||
|
String _libGccWorkaround(String buildDir, Version ndkVersion) {
|
||||||
|
final workaroundDir = path.join(
|
||||||
|
buildDir,
|
||||||
|
'cargokit',
|
||||||
|
'libgcc_workaround',
|
||||||
|
'${ndkVersion.major}',
|
||||||
|
);
|
||||||
|
Directory(workaroundDir).createSync(recursive: true);
|
||||||
|
if (ndkVersion.major >= 23) {
|
||||||
|
File(path.join(workaroundDir, 'libgcc.a'))
|
||||||
|
.writeAsStringSync('INPUT(-lunwind)');
|
||||||
|
} else {
|
||||||
|
// Other way around, untested, forward libgcc.a from libunwind once Rust
|
||||||
|
// gets updated for NDK23+.
|
||||||
|
File(path.join(workaroundDir, 'libunwind.a'))
|
||||||
|
.writeAsStringSync('INPUT(-lgcc)');
|
||||||
|
}
|
||||||
|
|
||||||
|
var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? '';
|
||||||
|
if (rustFlags.isNotEmpty) {
|
||||||
|
rustFlags = '$rustFlags\x1f';
|
||||||
|
}
|
||||||
|
rustFlags = '$rustFlags-L\x1f$workaroundDir';
|
||||||
|
return rustFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'crate_hash.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'precompile_binaries.dart';
|
||||||
|
import 'rustup.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
|
||||||
|
class Artifact {
|
||||||
|
/// File system location of the artifact.
|
||||||
|
final String path;
|
||||||
|
|
||||||
|
/// Actual file name that the artifact should have in destination folder.
|
||||||
|
final String finalFileName;
|
||||||
|
|
||||||
|
AritifactType get type {
|
||||||
|
if (finalFileName.endsWith('.dll') ||
|
||||||
|
finalFileName.endsWith('.dll.lib') ||
|
||||||
|
finalFileName.endsWith('.pdb') ||
|
||||||
|
finalFileName.endsWith('.so') ||
|
||||||
|
finalFileName.endsWith('.dylib')) {
|
||||||
|
return AritifactType.dylib;
|
||||||
|
} else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) {
|
||||||
|
return AritifactType.staticlib;
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown artifact type for $finalFileName');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Artifact({
|
||||||
|
required this.path,
|
||||||
|
required this.finalFileName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final _log = Logger('artifacts_provider');
|
||||||
|
|
||||||
|
class ArtifactProvider {
|
||||||
|
ArtifactProvider({
|
||||||
|
required this.environment,
|
||||||
|
required this.userOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
final BuildEnvironment environment;
|
||||||
|
final CargokitUserOptions userOptions;
|
||||||
|
|
||||||
|
Future<Map<Target, List<Artifact>>> getArtifacts(List<Target> targets) async {
|
||||||
|
final result = await _getPrecompiledArtifacts(targets);
|
||||||
|
|
||||||
|
final pendingTargets = List.of(targets);
|
||||||
|
pendingTargets.removeWhere((element) => result.containsKey(element));
|
||||||
|
|
||||||
|
if (pendingTargets.isEmpty) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
final rustup = Rustup();
|
||||||
|
for (final target in targets) {
|
||||||
|
final builder = RustBuilder(target: target, environment: environment);
|
||||||
|
builder.prepare(rustup);
|
||||||
|
_log.info('Building ${environment.crateInfo.packageName} for $target');
|
||||||
|
final targetDir = await builder.build();
|
||||||
|
// For local build accept both static and dynamic libraries.
|
||||||
|
final artifactNames = <String>{
|
||||||
|
...getArtifactNames(
|
||||||
|
target: target,
|
||||||
|
libraryName: environment.crateInfo.packageName,
|
||||||
|
aritifactType: AritifactType.dylib,
|
||||||
|
remote: false,
|
||||||
|
),
|
||||||
|
...getArtifactNames(
|
||||||
|
target: target,
|
||||||
|
libraryName: environment.crateInfo.packageName,
|
||||||
|
aritifactType: AritifactType.staticlib,
|
||||||
|
remote: false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
final artifacts = artifactNames
|
||||||
|
.map((artifactName) => Artifact(
|
||||||
|
path: path.join(targetDir, artifactName),
|
||||||
|
finalFileName: artifactName,
|
||||||
|
))
|
||||||
|
.where((element) => File(element.path).existsSync())
|
||||||
|
.toList();
|
||||||
|
result[target] = artifacts;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<Target, List<Artifact>>> _getPrecompiledArtifacts(
|
||||||
|
List<Target> targets) async {
|
||||||
|
if (userOptions.usePrecompiledBinaries == false) {
|
||||||
|
_log.info('Precompiled binaries are disabled');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (environment.crateOptions.precompiledBinaries == null) {
|
||||||
|
_log.fine('Precompiled binaries not enabled for this crate');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
final start = Stopwatch()..start();
|
||||||
|
final crateHash = CrateHash.compute(environment.manifestDir,
|
||||||
|
tempStorage: environment.targetTempDir);
|
||||||
|
_log.fine(
|
||||||
|
'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms');
|
||||||
|
|
||||||
|
final downloadedArtifactsDir =
|
||||||
|
path.join(environment.targetTempDir, 'precompiled', crateHash);
|
||||||
|
Directory(downloadedArtifactsDir).createSync(recursive: true);
|
||||||
|
|
||||||
|
final res = <Target, List<Artifact>>{};
|
||||||
|
|
||||||
|
for (final target in targets) {
|
||||||
|
final requiredArtifacts = getArtifactNames(
|
||||||
|
target: target,
|
||||||
|
libraryName: environment.crateInfo.packageName,
|
||||||
|
remote: true,
|
||||||
|
);
|
||||||
|
final artifactsForTarget = <Artifact>[];
|
||||||
|
|
||||||
|
for (final artifact in requiredArtifacts) {
|
||||||
|
final fileName = PrecompileBinaries.fileName(target, artifact);
|
||||||
|
final downloadedPath = path.join(downloadedArtifactsDir, fileName);
|
||||||
|
if (!File(downloadedPath).existsSync()) {
|
||||||
|
final signatureFileName =
|
||||||
|
PrecompileBinaries.signatureFileName(target, artifact);
|
||||||
|
await _tryDownloadArtifacts(
|
||||||
|
crateHash: crateHash,
|
||||||
|
fileName: fileName,
|
||||||
|
signatureFileName: signatureFileName,
|
||||||
|
finalPath: downloadedPath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (File(downloadedPath).existsSync()) {
|
||||||
|
artifactsForTarget.add(Artifact(
|
||||||
|
path: downloadedPath,
|
||||||
|
finalFileName: artifact,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only provide complete set of artifacts.
|
||||||
|
if (artifactsForTarget.length == requiredArtifacts.length) {
|
||||||
|
_log.fine('Found precompiled artifacts for $target');
|
||||||
|
res[target] = artifactsForTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Response> _get(Uri url, {Map<String, String>? headers}) async {
|
||||||
|
int attempt = 0;
|
||||||
|
const maxAttempts = 10;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
return await get(url, headers: headers);
|
||||||
|
} on SocketException catch (e) {
|
||||||
|
// Try to detect reset by peer error and retry.
|
||||||
|
if (attempt++ < maxAttempts &&
|
||||||
|
(e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) {
|
||||||
|
_log.severe(
|
||||||
|
'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...');
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _tryDownloadArtifacts({
|
||||||
|
required String crateHash,
|
||||||
|
required String fileName,
|
||||||
|
required String signatureFileName,
|
||||||
|
required String finalPath,
|
||||||
|
}) async {
|
||||||
|
final precompiledBinaries = environment.crateOptions.precompiledBinaries!;
|
||||||
|
final prefix = precompiledBinaries.uriPrefix;
|
||||||
|
final url = Uri.parse('$prefix$crateHash/$fileName');
|
||||||
|
final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName');
|
||||||
|
_log.fine('Downloading signature from $signatureUrl');
|
||||||
|
final signature = await _get(signatureUrl);
|
||||||
|
if (signature.statusCode == 404) {
|
||||||
|
_log.warning(
|
||||||
|
'Precompiled binaries not available for crate hash $crateHash ($fileName)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (signature.statusCode != 200) {
|
||||||
|
_log.severe(
|
||||||
|
'Failed to download signature $signatureUrl: status ${signature.statusCode}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_log.fine('Downloading binary from $url');
|
||||||
|
final res = await _get(url);
|
||||||
|
if (res.statusCode != 200) {
|
||||||
|
_log.severe('Failed to download binary $url: status ${res.statusCode}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (verify(
|
||||||
|
precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) {
|
||||||
|
File(finalPath).writeAsBytesSync(res.bodyBytes);
|
||||||
|
} else {
|
||||||
|
_log.shout('Signature verification failed! Ignoring binary.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AritifactType {
|
||||||
|
staticlib,
|
||||||
|
dylib,
|
||||||
|
}
|
||||||
|
|
||||||
|
AritifactType artifactTypeForTarget(Target target) {
|
||||||
|
if (target.darwinPlatform != null) {
|
||||||
|
return AritifactType.staticlib;
|
||||||
|
} else {
|
||||||
|
return AritifactType.dylib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getArtifactNames({
|
||||||
|
required Target target,
|
||||||
|
required String libraryName,
|
||||||
|
required bool remote,
|
||||||
|
AritifactType? aritifactType,
|
||||||
|
}) {
|
||||||
|
aritifactType ??= artifactTypeForTarget(target);
|
||||||
|
if (target.darwinArch != null) {
|
||||||
|
if (aritifactType == AritifactType.staticlib) {
|
||||||
|
return ['lib$libraryName.a'];
|
||||||
|
} else {
|
||||||
|
return ['lib$libraryName.dylib'];
|
||||||
|
}
|
||||||
|
} else if (target.rust.contains('-windows-')) {
|
||||||
|
if (aritifactType == AritifactType.staticlib) {
|
||||||
|
return ['$libraryName.lib'];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'$libraryName.dll',
|
||||||
|
'$libraryName.dll.lib',
|
||||||
|
if (!remote) '$libraryName.pdb'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else if (target.rust.contains('-linux-')) {
|
||||||
|
if (aritifactType == AritifactType.staticlib) {
|
||||||
|
return ['lib$libraryName.a'];
|
||||||
|
} else {
|
||||||
|
return ['lib$libraryName.so'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Exception("Unsupported target: ${target.rust}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'artifacts_provider.dart';
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'environment.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
|
||||||
|
class BuildCMake {
|
||||||
|
final CargokitUserOptions userOptions;
|
||||||
|
|
||||||
|
BuildCMake({required this.userOptions});
|
||||||
|
|
||||||
|
Future<void> build() async {
|
||||||
|
final targetPlatform = Environment.targetPlatform;
|
||||||
|
final target = Target.forFlutterName(Environment.targetPlatform);
|
||||||
|
if (target == null) {
|
||||||
|
throw Exception("Unknown target platform: $targetPlatform");
|
||||||
|
}
|
||||||
|
|
||||||
|
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
|
||||||
|
final provider =
|
||||||
|
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||||
|
final artifacts = await provider.getArtifacts([target]);
|
||||||
|
|
||||||
|
final libs = artifacts[target]!;
|
||||||
|
|
||||||
|
for (final lib in libs) {
|
||||||
|
if (lib.type == AritifactType.dylib) {
|
||||||
|
File(lib.path)
|
||||||
|
.copySync(path.join(Environment.outputDir, lib.finalFileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'artifacts_provider.dart';
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'environment.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
|
||||||
|
final log = Logger('build_gradle');
|
||||||
|
|
||||||
|
class BuildGradle {
|
||||||
|
BuildGradle({required this.userOptions});
|
||||||
|
|
||||||
|
final CargokitUserOptions userOptions;
|
||||||
|
|
||||||
|
Future<void> build() async {
|
||||||
|
final targets = Environment.targetPlatforms.map((arch) {
|
||||||
|
final target = Target.forFlutterName(arch);
|
||||||
|
if (target == null) {
|
||||||
|
throw Exception(
|
||||||
|
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
final environment = BuildEnvironment.fromEnvironment(isAndroid: true);
|
||||||
|
final provider =
|
||||||
|
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||||
|
final artifacts = await provider.getArtifacts(targets);
|
||||||
|
|
||||||
|
for (final target in targets) {
|
||||||
|
final libs = artifacts[target]!;
|
||||||
|
final outputDir = path.join(Environment.outputDir, target.android!);
|
||||||
|
Directory(outputDir).createSync(recursive: true);
|
||||||
|
|
||||||
|
for (final lib in libs) {
|
||||||
|
if (lib.type == AritifactType.dylib) {
|
||||||
|
File(lib.path).copySync(path.join(outputDir, lib.finalFileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'artifacts_provider.dart';
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'environment.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
class BuildPod {
|
||||||
|
BuildPod({required this.userOptions});
|
||||||
|
|
||||||
|
final CargokitUserOptions userOptions;
|
||||||
|
|
||||||
|
Future<void> build() async {
|
||||||
|
final targets = Environment.darwinArchs.map((arch) {
|
||||||
|
final target = Target.forDarwin(
|
||||||
|
platformName: Environment.darwinPlatformName, darwinAarch: arch);
|
||||||
|
if (target == null) {
|
||||||
|
throw Exception(
|
||||||
|
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
|
||||||
|
final provider =
|
||||||
|
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||||
|
final artifacts = await provider.getArtifacts(targets);
|
||||||
|
|
||||||
|
void performLipo(String targetFile, Iterable<String> sourceFiles) {
|
||||||
|
runCommand("lipo", [
|
||||||
|
'-create',
|
||||||
|
...sourceFiles,
|
||||||
|
'-output',
|
||||||
|
targetFile,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
final outputDir = Environment.outputDir;
|
||||||
|
|
||||||
|
Directory(outputDir).createSync(recursive: true);
|
||||||
|
|
||||||
|
final staticLibs = artifacts.values
|
||||||
|
.expand((element) => element)
|
||||||
|
.where((element) => element.type == AritifactType.staticlib)
|
||||||
|
.toList();
|
||||||
|
final dynamicLibs = artifacts.values
|
||||||
|
.expand((element) => element)
|
||||||
|
.where((element) => element.type == AritifactType.dylib)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final libName = environment.crateInfo.packageName;
|
||||||
|
|
||||||
|
// If there is static lib, use it and link it with pod
|
||||||
|
if (staticLibs.isNotEmpty) {
|
||||||
|
final finalTargetFile = path.join(outputDir, "lib$libName.a");
|
||||||
|
performLipo(finalTargetFile, staticLibs.map((e) => e.path));
|
||||||
|
} else {
|
||||||
|
// Otherwise try to replace bundle dylib with our dylib
|
||||||
|
final bundlePaths = [
|
||||||
|
'$libName.framework/Versions/A/$libName',
|
||||||
|
'$libName.framework/$libName',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final bundlePath in bundlePaths) {
|
||||||
|
final targetFile = path.join(outputDir, bundlePath);
|
||||||
|
if (File(targetFile).existsSync()) {
|
||||||
|
performLipo(targetFile, dynamicLibs.map((e) => e.path));
|
||||||
|
|
||||||
|
// Replace absolute id with @rpath one so that it works properly
|
||||||
|
// when moved to Frameworks.
|
||||||
|
runCommand("install_name_tool", [
|
||||||
|
'-id',
|
||||||
|
'@rpath/$bundlePath',
|
||||||
|
targetFile,
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw Exception('Unable to find bundle for dynamic library');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
import 'package:github/github.dart';
|
||||||
|
import 'package:hex/hex.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
import 'android_environment.dart';
|
||||||
|
import 'build_cmake.dart';
|
||||||
|
import 'build_gradle.dart';
|
||||||
|
import 'build_pod.dart';
|
||||||
|
import 'logging.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'precompile_binaries.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
import 'util.dart';
|
||||||
|
import 'verify_binaries.dart';
|
||||||
|
|
||||||
|
final log = Logger('build_tool');
|
||||||
|
|
||||||
|
abstract class BuildCommand extends Command {
|
||||||
|
Future<void> runBuildCommand(CargokitUserOptions options);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> run() async {
|
||||||
|
final options = CargokitUserOptions.load();
|
||||||
|
|
||||||
|
if (options.verboseLogging ||
|
||||||
|
Platform.environment['CARGOKIT_VERBOSE'] == '1') {
|
||||||
|
enableVerboseLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
await runBuildCommand(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildPodCommand extends BuildCommand {
|
||||||
|
@override
|
||||||
|
final name = 'build-pod';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Build cocoa pod library';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||||
|
final build = BuildPod(userOptions: options);
|
||||||
|
await build.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildGradleCommand extends BuildCommand {
|
||||||
|
@override
|
||||||
|
final name = 'build-gradle';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Build android library';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||||
|
final build = BuildGradle(userOptions: options);
|
||||||
|
await build.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildCMakeCommand extends BuildCommand {
|
||||||
|
@override
|
||||||
|
final name = 'build-cmake';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Build CMake library';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||||
|
final build = BuildCMake(userOptions: options);
|
||||||
|
await build.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenKeyCommand extends Command {
|
||||||
|
@override
|
||||||
|
final name = 'gen-key';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Generate key pair for signing precompiled binaries';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
final kp = generateKey();
|
||||||
|
final private = HEX.encode(kp.privateKey.bytes);
|
||||||
|
final public = HEX.encode(kp.publicKey.bytes);
|
||||||
|
print("Private Key: $private");
|
||||||
|
print("Public Key: $public");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrecompileBinariesCommand extends Command {
|
||||||
|
PrecompileBinariesCommand() {
|
||||||
|
argParser
|
||||||
|
..addOption(
|
||||||
|
'repository',
|
||||||
|
mandatory: true,
|
||||||
|
help: 'Github repository slug in format owner/name',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'manifest-dir',
|
||||||
|
mandatory: true,
|
||||||
|
help: 'Directory containing Cargo.toml',
|
||||||
|
)
|
||||||
|
..addMultiOption('target',
|
||||||
|
help: 'Rust target triple of artifact to build.\n'
|
||||||
|
'Can be specified multiple times or omitted in which case\n'
|
||||||
|
'all targets for current platform will be built.')
|
||||||
|
..addOption(
|
||||||
|
'android-sdk-location',
|
||||||
|
help: 'Location of Android SDK (if available)',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'android-ndk-version',
|
||||||
|
help: 'Android NDK version (if available)',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'android-min-sdk-version',
|
||||||
|
help: 'Android minimum rquired version (if available)',
|
||||||
|
)
|
||||||
|
..addOption(
|
||||||
|
'temp-dir',
|
||||||
|
help: 'Directory to store temporary build artifacts',
|
||||||
|
)
|
||||||
|
..addFlag(
|
||||||
|
"verbose",
|
||||||
|
abbr: "v",
|
||||||
|
defaultsTo: false,
|
||||||
|
help: "Enable verbose logging",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final name = 'precompile-binaries';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Prebuild and upload binaries\n'
|
||||||
|
'Private key must be passed through PRIVATE_KEY environment variable. '
|
||||||
|
'Use gen_key through generate priave key.\n'
|
||||||
|
'Github token must be passed as GITHUB_TOKEN environment variable.\n';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> run() async {
|
||||||
|
final verbose = argResults!['verbose'] as bool;
|
||||||
|
if (verbose) {
|
||||||
|
enableVerboseLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
final privateKeyString = Platform.environment['PRIVATE_KEY'];
|
||||||
|
if (privateKeyString == null) {
|
||||||
|
throw ArgumentError('Missing PRIVATE_KEY environment variable');
|
||||||
|
}
|
||||||
|
final githubToken = Platform.environment['GITHUB_TOKEN'];
|
||||||
|
if (githubToken == null) {
|
||||||
|
throw ArgumentError('Missing GITHUB_TOKEN environment variable');
|
||||||
|
}
|
||||||
|
final privateKey = HEX.decode(privateKeyString);
|
||||||
|
if (privateKey.length != 64) {
|
||||||
|
throw ArgumentError('Private key must be 64 bytes long');
|
||||||
|
}
|
||||||
|
final manifestDir = argResults!['manifest-dir'] as String;
|
||||||
|
if (!Directory(manifestDir).existsSync()) {
|
||||||
|
throw ArgumentError('Manifest directory does not exist: $manifestDir');
|
||||||
|
}
|
||||||
|
String? androidMinSdkVersionString =
|
||||||
|
argResults!['android-min-sdk-version'] as String?;
|
||||||
|
int? androidMinSdkVersion;
|
||||||
|
if (androidMinSdkVersionString != null) {
|
||||||
|
androidMinSdkVersion = int.tryParse(androidMinSdkVersionString);
|
||||||
|
if (androidMinSdkVersion == null) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid android-min-sdk-version: $androidMinSdkVersionString');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final targetStrigns = argResults!['target'] as List<String>;
|
||||||
|
final targets = targetStrigns.map((target) {
|
||||||
|
final res = Target.forRustTriple(target);
|
||||||
|
if (res == null) {
|
||||||
|
throw ArgumentError('Invalid target: $target');
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}).toList(growable: false);
|
||||||
|
final precompileBinaries = PrecompileBinaries(
|
||||||
|
privateKey: PrivateKey(privateKey),
|
||||||
|
githubToken: githubToken,
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
repositorySlug: RepositorySlug.full(argResults!['repository'] as String),
|
||||||
|
targets: targets,
|
||||||
|
androidSdkLocation: argResults!['android-sdk-location'] as String?,
|
||||||
|
androidNdkVersion: argResults!['android-ndk-version'] as String?,
|
||||||
|
androidMinSdkVersion: androidMinSdkVersion,
|
||||||
|
tempDir: argResults!['temp-dir'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
await precompileBinaries.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VerifyBinariesCommand extends Command {
|
||||||
|
VerifyBinariesCommand() {
|
||||||
|
argParser.addOption(
|
||||||
|
'manifest-dir',
|
||||||
|
mandatory: true,
|
||||||
|
help: 'Directory containing Cargo.toml',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final name = "verify-binaries";
|
||||||
|
|
||||||
|
@override
|
||||||
|
final description = 'Verifies published binaries\n'
|
||||||
|
'Checks whether there is a binary published for each targets\n'
|
||||||
|
'and checks the signature.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> run() async {
|
||||||
|
final manifestDir = argResults!['manifest-dir'] as String;
|
||||||
|
final verifyBinaries = VerifyBinaries(
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
);
|
||||||
|
await verifyBinaries.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> runMain(List<String> args) async {
|
||||||
|
try {
|
||||||
|
// Init logging before options are loaded
|
||||||
|
initLogging();
|
||||||
|
|
||||||
|
if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) {
|
||||||
|
return AndroidEnvironment.clangLinkerWrapper(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
final runner = CommandRunner('build_tool', 'Cargokit built_tool')
|
||||||
|
..addCommand(BuildPodCommand())
|
||||||
|
..addCommand(BuildGradleCommand())
|
||||||
|
..addCommand(BuildCMakeCommand())
|
||||||
|
..addCommand(GenKeyCommand())
|
||||||
|
..addCommand(PrecompileBinariesCommand())
|
||||||
|
..addCommand(VerifyBinariesCommand());
|
||||||
|
|
||||||
|
await runner.run(args);
|
||||||
|
} on ArgumentError catch (e) {
|
||||||
|
stderr.writeln(e.toString());
|
||||||
|
exit(1);
|
||||||
|
} catch (e, s) {
|
||||||
|
log.severe(kDoubleSeparator);
|
||||||
|
log.severe('Cargokit BuildTool failed with error:');
|
||||||
|
log.severe(kSeparator);
|
||||||
|
log.severe(e);
|
||||||
|
// This tells user to install Rust, there's no need to pollute the log with
|
||||||
|
// stack trace.
|
||||||
|
if (e is! RustupNotFoundException) {
|
||||||
|
log.severe(kSeparator);
|
||||||
|
log.severe(s);
|
||||||
|
log.severe(kSeparator);
|
||||||
|
log.severe('BuildTool arguments: $args');
|
||||||
|
}
|
||||||
|
log.severe(kDoubleSeparator);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'android_environment.dart';
|
||||||
|
import 'cargo.dart';
|
||||||
|
import 'environment.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'rustup.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
final _log = Logger('builder');
|
||||||
|
|
||||||
|
enum BuildConfiguration {
|
||||||
|
debug,
|
||||||
|
release,
|
||||||
|
profile,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on BuildConfiguration {
|
||||||
|
bool get isDebug => this == BuildConfiguration.debug;
|
||||||
|
String get rustName => switch (this) {
|
||||||
|
BuildConfiguration.debug => 'debug',
|
||||||
|
BuildConfiguration.release => 'release',
|
||||||
|
BuildConfiguration.profile => 'release',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildException implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
BuildException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'BuildException: $message';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildEnvironment {
|
||||||
|
final BuildConfiguration configuration;
|
||||||
|
final CargokitCrateOptions crateOptions;
|
||||||
|
final String targetTempDir;
|
||||||
|
final String manifestDir;
|
||||||
|
final CrateInfo crateInfo;
|
||||||
|
|
||||||
|
final bool isAndroid;
|
||||||
|
final String? androidSdkPath;
|
||||||
|
final String? androidNdkVersion;
|
||||||
|
final int? androidMinSdkVersion;
|
||||||
|
final String? javaHome;
|
||||||
|
|
||||||
|
BuildEnvironment({
|
||||||
|
required this.configuration,
|
||||||
|
required this.crateOptions,
|
||||||
|
required this.targetTempDir,
|
||||||
|
required this.manifestDir,
|
||||||
|
required this.crateInfo,
|
||||||
|
required this.isAndroid,
|
||||||
|
this.androidSdkPath,
|
||||||
|
this.androidNdkVersion,
|
||||||
|
this.androidMinSdkVersion,
|
||||||
|
this.javaHome,
|
||||||
|
});
|
||||||
|
|
||||||
|
static BuildConfiguration parseBuildConfiguration(String value) {
|
||||||
|
// XCode configuration adds the flavor to configuration name.
|
||||||
|
final firstSegment = value.split('-').first;
|
||||||
|
final buildConfiguration = BuildConfiguration.values.firstWhereOrNull(
|
||||||
|
(e) => e.name == firstSegment,
|
||||||
|
);
|
||||||
|
if (buildConfiguration == null) {
|
||||||
|
_log.warning('Unknown build configuraiton $value, will assume release');
|
||||||
|
return BuildConfiguration.release;
|
||||||
|
}
|
||||||
|
return buildConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildEnvironment fromEnvironment({
|
||||||
|
required bool isAndroid,
|
||||||
|
}) {
|
||||||
|
final buildConfiguration =
|
||||||
|
parseBuildConfiguration(Environment.configuration);
|
||||||
|
final manifestDir = Environment.manifestDir;
|
||||||
|
final crateOptions = CargokitCrateOptions.load(
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
);
|
||||||
|
final crateInfo = CrateInfo.load(manifestDir);
|
||||||
|
return BuildEnvironment(
|
||||||
|
configuration: buildConfiguration,
|
||||||
|
crateOptions: crateOptions,
|
||||||
|
targetTempDir: Environment.targetTempDir,
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
crateInfo: crateInfo,
|
||||||
|
isAndroid: isAndroid,
|
||||||
|
androidSdkPath: isAndroid ? Environment.sdkPath : null,
|
||||||
|
androidNdkVersion: isAndroid ? Environment.ndkVersion : null,
|
||||||
|
androidMinSdkVersion:
|
||||||
|
isAndroid ? int.parse(Environment.minSdkVersion) : null,
|
||||||
|
javaHome: isAndroid ? Environment.javaHome : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RustBuilder {
|
||||||
|
final Target target;
|
||||||
|
final BuildEnvironment environment;
|
||||||
|
|
||||||
|
RustBuilder({
|
||||||
|
required this.target,
|
||||||
|
required this.environment,
|
||||||
|
});
|
||||||
|
|
||||||
|
void prepare(
|
||||||
|
Rustup rustup,
|
||||||
|
) {
|
||||||
|
final toolchain = _toolchain;
|
||||||
|
if (rustup.installedTargets(toolchain) == null) {
|
||||||
|
rustup.installToolchain(toolchain);
|
||||||
|
}
|
||||||
|
if (toolchain == 'nightly') {
|
||||||
|
rustup.installRustSrcForNightly();
|
||||||
|
}
|
||||||
|
if (!rustup.installedTargets(toolchain)!.contains(target.rust)) {
|
||||||
|
rustup.installTarget(target.rust, toolchain: toolchain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CargoBuildOptions? get _buildOptions =>
|
||||||
|
environment.crateOptions.cargo[environment.configuration];
|
||||||
|
|
||||||
|
String get _toolchain => _buildOptions?.toolchain.name ?? 'stable';
|
||||||
|
|
||||||
|
/// Returns the path of directory containing build artifacts.
|
||||||
|
Future<String> build() async {
|
||||||
|
final extraArgs = _buildOptions?.flags ?? [];
|
||||||
|
final manifestPath = path.join(environment.manifestDir, 'Cargo.toml');
|
||||||
|
runCommand(
|
||||||
|
'rustup',
|
||||||
|
[
|
||||||
|
'run',
|
||||||
|
_toolchain,
|
||||||
|
'cargo',
|
||||||
|
'build',
|
||||||
|
...extraArgs,
|
||||||
|
'--manifest-path',
|
||||||
|
manifestPath,
|
||||||
|
'-p',
|
||||||
|
environment.crateInfo.packageName,
|
||||||
|
if (!environment.configuration.isDebug) '--release',
|
||||||
|
'--target',
|
||||||
|
target.rust,
|
||||||
|
'--target-dir',
|
||||||
|
environment.targetTempDir,
|
||||||
|
],
|
||||||
|
environment: await _buildEnvironment(),
|
||||||
|
);
|
||||||
|
return path.join(
|
||||||
|
environment.targetTempDir,
|
||||||
|
target.rust,
|
||||||
|
environment.configuration.rustName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> _buildEnvironment() async {
|
||||||
|
if (target.android == null) {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
final sdkPath = environment.androidSdkPath;
|
||||||
|
final ndkVersion = environment.androidNdkVersion;
|
||||||
|
final minSdkVersion = environment.androidMinSdkVersion;
|
||||||
|
if (sdkPath == null) {
|
||||||
|
throw BuildException('androidSdkPath is not set');
|
||||||
|
}
|
||||||
|
if (ndkVersion == null) {
|
||||||
|
throw BuildException('androidNdkVersion is not set');
|
||||||
|
}
|
||||||
|
if (minSdkVersion == null) {
|
||||||
|
throw BuildException('androidMinSdkVersion is not set');
|
||||||
|
}
|
||||||
|
final env = AndroidEnvironment(
|
||||||
|
sdkPath: sdkPath,
|
||||||
|
ndkVersion: ndkVersion,
|
||||||
|
minSdkVersion: minSdkVersion,
|
||||||
|
targetTempDir: environment.targetTempDir,
|
||||||
|
target: target,
|
||||||
|
);
|
||||||
|
if (!env.ndkIsInstalled() && environment.javaHome != null) {
|
||||||
|
env.installNdk(javaHome: environment.javaHome!);
|
||||||
|
}
|
||||||
|
return env.buildEnvironment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:toml/toml.dart';
|
||||||
|
|
||||||
|
class ManifestException {
|
||||||
|
ManifestException(this.message, {required this.fileName});
|
||||||
|
|
||||||
|
final String? fileName;
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
if (fileName != null) {
|
||||||
|
return 'Failed to parse package manifest at $fileName: $message';
|
||||||
|
} else {
|
||||||
|
return 'Failed to parse package manifest: $message';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CrateInfo {
|
||||||
|
CrateInfo({required this.packageName});
|
||||||
|
|
||||||
|
final String packageName;
|
||||||
|
|
||||||
|
static CrateInfo parseManifest(String manifest, {final String? fileName}) {
|
||||||
|
final toml = TomlDocument.parse(manifest);
|
||||||
|
final package = toml.toMap()['package'];
|
||||||
|
if (package == null) {
|
||||||
|
throw ManifestException('Missing package section', fileName: fileName);
|
||||||
|
}
|
||||||
|
final name = package['name'];
|
||||||
|
if (name == null) {
|
||||||
|
throw ManifestException('Missing package name', fileName: fileName);
|
||||||
|
}
|
||||||
|
return CrateInfo(packageName: name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CrateInfo load(String manifestDir) {
|
||||||
|
final manifestFile = File(path.join(manifestDir, 'Cargo.toml'));
|
||||||
|
final manifest = manifestFile.readAsStringSync();
|
||||||
|
return parseManifest(manifest, fileName: manifestFile.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
class CrateHash {
|
||||||
|
/// Computes a hash uniquely identifying crate content. This takes into account
|
||||||
|
/// content all all .rs files inside the src directory, as well as Cargo.toml,
|
||||||
|
/// Cargo.lock, build.rs and cargokit.yaml.
|
||||||
|
///
|
||||||
|
/// If [tempStorage] is provided, computed hash is stored in a file in that directory
|
||||||
|
/// and reused on subsequent calls if the crate content hasn't changed.
|
||||||
|
static String compute(String manifestDir, {String? tempStorage}) {
|
||||||
|
return CrateHash._(
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
tempStorage: tempStorage,
|
||||||
|
)._compute();
|
||||||
|
}
|
||||||
|
|
||||||
|
CrateHash._({
|
||||||
|
required this.manifestDir,
|
||||||
|
required this.tempStorage,
|
||||||
|
});
|
||||||
|
|
||||||
|
String _compute() {
|
||||||
|
final files = getFiles();
|
||||||
|
final tempStorage = this.tempStorage;
|
||||||
|
if (tempStorage != null) {
|
||||||
|
final quickHash = _computeQuickHash(files);
|
||||||
|
final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash'));
|
||||||
|
quickHashFolder.createSync(recursive: true);
|
||||||
|
final quickHashFile = File(path.join(quickHashFolder.path, quickHash));
|
||||||
|
if (quickHashFile.existsSync()) {
|
||||||
|
return quickHashFile.readAsStringSync();
|
||||||
|
}
|
||||||
|
final hash = _computeHash(files);
|
||||||
|
quickHashFile.writeAsStringSync(hash);
|
||||||
|
return hash;
|
||||||
|
} else {
|
||||||
|
return _computeHash(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes a quick hash based on files stat (without reading contents). This
|
||||||
|
/// is used to cache the real hash, which is slower to compute since it involves
|
||||||
|
/// reading every single file.
|
||||||
|
String _computeQuickHash(List<File> files) {
|
||||||
|
final output = AccumulatorSink<Digest>();
|
||||||
|
final input = sha256.startChunkedConversion(output);
|
||||||
|
|
||||||
|
final data = ByteData(8);
|
||||||
|
for (final file in files) {
|
||||||
|
input.add(utf8.encode(file.path));
|
||||||
|
final stat = file.statSync();
|
||||||
|
data.setUint64(0, stat.size);
|
||||||
|
input.add(data.buffer.asUint8List());
|
||||||
|
data.setUint64(0, stat.modified.millisecondsSinceEpoch);
|
||||||
|
input.add(data.buffer.asUint8List());
|
||||||
|
}
|
||||||
|
|
||||||
|
input.close();
|
||||||
|
return base64Url.encode(output.events.single.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _computeHash(List<File> files) {
|
||||||
|
final output = AccumulatorSink<Digest>();
|
||||||
|
final input = sha256.startChunkedConversion(output);
|
||||||
|
|
||||||
|
void addTextFile(File file) {
|
||||||
|
// text Files are hashed by lines in case we're dealing with github checkout
|
||||||
|
// that auto-converts line endings.
|
||||||
|
final splitter = LineSplitter();
|
||||||
|
if (file.existsSync()) {
|
||||||
|
final data = file.readAsStringSync();
|
||||||
|
final lines = splitter.convert(data);
|
||||||
|
for (final line in lines) {
|
||||||
|
input.add(utf8.encode(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final file in files) {
|
||||||
|
addTextFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
input.close();
|
||||||
|
final res = output.events.single;
|
||||||
|
|
||||||
|
// Truncate to 128bits.
|
||||||
|
final hash = res.bytes.sublist(0, 16);
|
||||||
|
return hex.encode(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<File> getFiles() {
|
||||||
|
final src = Directory(path.join(manifestDir, 'src'));
|
||||||
|
final files = src
|
||||||
|
.listSync(recursive: true, followLinks: false)
|
||||||
|
.whereType<File>()
|
||||||
|
.toList();
|
||||||
|
files.sortBy((element) => element.path);
|
||||||
|
void addFile(String relative) {
|
||||||
|
final file = File(path.join(manifestDir, relative));
|
||||||
|
if (file.existsSync()) {
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addFile('Cargo.toml');
|
||||||
|
addFile('Cargo.lock');
|
||||||
|
addFile('build.rs');
|
||||||
|
addFile('cargokit.yaml');
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String manifestDir;
|
||||||
|
final String? tempStorage;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
extension on String {
|
||||||
|
String resolveSymlink() => File(this).resolveSymbolicLinksSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Environment {
|
||||||
|
/// Current build configuration (debug or release).
|
||||||
|
static String get configuration =>
|
||||||
|
_getEnv("CARGOKIT_CONFIGURATION").toLowerCase();
|
||||||
|
|
||||||
|
static bool get isDebug => configuration == 'debug';
|
||||||
|
static bool get isRelease => configuration == 'release';
|
||||||
|
|
||||||
|
/// Temporary directory where Rust build artifacts are placed.
|
||||||
|
static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR");
|
||||||
|
|
||||||
|
/// Final output directory where the build artifacts are placed.
|
||||||
|
static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR');
|
||||||
|
|
||||||
|
/// Path to the crate manifest (containing Cargo.toml).
|
||||||
|
static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR');
|
||||||
|
|
||||||
|
/// Directory inside root project. Not necessarily root folder. Symlinks are
|
||||||
|
/// not resolved on purpose.
|
||||||
|
static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR');
|
||||||
|
|
||||||
|
// Pod
|
||||||
|
|
||||||
|
/// Platform name (macosx, iphoneos, iphonesimulator).
|
||||||
|
static String get darwinPlatformName =>
|
||||||
|
_getEnv("CARGOKIT_DARWIN_PLATFORM_NAME");
|
||||||
|
|
||||||
|
/// List of architectures to build for (arm64, armv7, x86_64).
|
||||||
|
static List<String> get darwinArchs =>
|
||||||
|
_getEnv("CARGOKIT_DARWIN_ARCHS").split(' ');
|
||||||
|
|
||||||
|
// Gradle
|
||||||
|
static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION");
|
||||||
|
static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION");
|
||||||
|
static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR");
|
||||||
|
static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME");
|
||||||
|
static List<String> get targetPlatforms =>
|
||||||
|
_getEnv("CARGOKIT_TARGET_PLATFORMS").split(',');
|
||||||
|
|
||||||
|
// CMAKE
|
||||||
|
static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM");
|
||||||
|
|
||||||
|
static String _getEnv(String key) {
|
||||||
|
final res = Platform.environment[key];
|
||||||
|
if (res == null) {
|
||||||
|
throw Exception("Missing environment variable $key");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _getEnvPath(String key) {
|
||||||
|
final res = _getEnv(key);
|
||||||
|
if (Directory(res).existsSync()) {
|
||||||
|
return res.resolveSymlink();
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
const String kSeparator = "--";
|
||||||
|
const String kDoubleSeparator = "==";
|
||||||
|
|
||||||
|
bool _lastMessageWasSeparator = false;
|
||||||
|
|
||||||
|
void _log(LogRecord rec) {
|
||||||
|
final prefix = '${rec.level.name}: ';
|
||||||
|
final out = rec.level == Level.SEVERE ? stderr : stdout;
|
||||||
|
if (rec.message == kSeparator) {
|
||||||
|
if (!_lastMessageWasSeparator) {
|
||||||
|
out.write(prefix);
|
||||||
|
out.writeln('-' * 80);
|
||||||
|
_lastMessageWasSeparator = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (rec.message == kDoubleSeparator) {
|
||||||
|
out.write(prefix);
|
||||||
|
out.writeln('=' * 80);
|
||||||
|
_lastMessageWasSeparator = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.write(prefix);
|
||||||
|
out.writeln(rec.message);
|
||||||
|
_lastMessageWasSeparator = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initLogging() {
|
||||||
|
Logger.root.level = Level.INFO;
|
||||||
|
Logger.root.onRecord.listen((LogRecord rec) {
|
||||||
|
final lines = rec.message.split('\n');
|
||||||
|
for (final line in lines) {
|
||||||
|
if (line.isNotEmpty || lines.length == 1 || line != lines.last) {
|
||||||
|
_log(LogRecord(
|
||||||
|
rec.level,
|
||||||
|
line,
|
||||||
|
rec.loggerName,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableVerboseLogging() {
|
||||||
|
Logger.root.level = Level.ALL;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
import 'package:hex/hex.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:source_span/source_span.dart';
|
||||||
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'environment.dart';
|
||||||
|
import 'rustup.dart';
|
||||||
|
|
||||||
|
final _log = Logger('options');
|
||||||
|
|
||||||
|
/// A class for exceptions that have source span information attached.
|
||||||
|
class SourceSpanException implements Exception {
|
||||||
|
// This is a getter so that subclasses can override it.
|
||||||
|
/// A message describing the exception.
|
||||||
|
String get message => _message;
|
||||||
|
final String _message;
|
||||||
|
|
||||||
|
// This is a getter so that subclasses can override it.
|
||||||
|
/// The span associated with this exception.
|
||||||
|
///
|
||||||
|
/// This may be `null` if the source location can't be determined.
|
||||||
|
SourceSpan? get span => _span;
|
||||||
|
final SourceSpan? _span;
|
||||||
|
|
||||||
|
SourceSpanException(this._message, this._span);
|
||||||
|
|
||||||
|
/// Returns a string representation of `this`.
|
||||||
|
///
|
||||||
|
/// [color] may either be a [String], a [bool], or `null`. If it's a string,
|
||||||
|
/// it indicates an ANSI terminal color escape that should be used to
|
||||||
|
/// highlight the span's text. If it's `true`, it indicates that the text
|
||||||
|
/// should be highlighted using the default color. If it's `false` or `null`,
|
||||||
|
/// it indicates that the text shouldn't be highlighted.
|
||||||
|
@override
|
||||||
|
String toString({Object? color}) {
|
||||||
|
if (span == null) return message;
|
||||||
|
return 'Error on ${span!.message(message, color: color)}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Toolchain {
|
||||||
|
stable,
|
||||||
|
beta,
|
||||||
|
nightly,
|
||||||
|
}
|
||||||
|
|
||||||
|
class CargoBuildOptions {
|
||||||
|
final Toolchain toolchain;
|
||||||
|
final List<String> flags;
|
||||||
|
|
||||||
|
CargoBuildOptions({
|
||||||
|
required this.toolchain,
|
||||||
|
required this.flags,
|
||||||
|
});
|
||||||
|
|
||||||
|
static Toolchain _toolchainFromNode(YamlNode node) {
|
||||||
|
if (node case YamlScalar(value: String name)) {
|
||||||
|
final toolchain =
|
||||||
|
Toolchain.values.firstWhereOrNull((element) => element.name == name);
|
||||||
|
if (toolchain != null) {
|
||||||
|
return toolchain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.',
|
||||||
|
node.span);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CargoBuildOptions parse(YamlNode node) {
|
||||||
|
if (node is! YamlMap) {
|
||||||
|
throw SourceSpanException('Cargo options must be a map', node.span);
|
||||||
|
}
|
||||||
|
Toolchain toolchain = Toolchain.stable;
|
||||||
|
List<String> flags = [];
|
||||||
|
for (final MapEntry(:key, :value) in node.nodes.entries) {
|
||||||
|
if (key case YamlScalar(value: 'toolchain')) {
|
||||||
|
toolchain = _toolchainFromNode(value);
|
||||||
|
} else if (key case YamlScalar(value: 'extra_flags')) {
|
||||||
|
if (value case YamlList(nodes: List<YamlNode> list)) {
|
||||||
|
if (list.every((element) {
|
||||||
|
if (element case YamlScalar(value: String _)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})) {
|
||||||
|
flags = list.map((e) => e.value as String).toList();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Extra flags must be a list of strings', value.span);
|
||||||
|
} else {
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Unknown cargo option type. Must be "toolchain" or "extra_flags".',
|
||||||
|
key.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CargoBuildOptions(toolchain: toolchain, flags: flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on YamlMap {
|
||||||
|
/// Map that extracts keys so that we can do map case check on them.
|
||||||
|
Map<dynamic, YamlNode> get valueMap =>
|
||||||
|
nodes.map((key, value) => MapEntry(key.value, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrecompiledBinaries {
|
||||||
|
final String uriPrefix;
|
||||||
|
final PublicKey publicKey;
|
||||||
|
|
||||||
|
PrecompiledBinaries({
|
||||||
|
required this.uriPrefix,
|
||||||
|
required this.publicKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
static PublicKey _publicKeyFromHex(String key, SourceSpan? span) {
|
||||||
|
final bytes = HEX.decode(key);
|
||||||
|
if (bytes.length != 32) {
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Invalid public key. Must be 32 bytes long.', span);
|
||||||
|
}
|
||||||
|
return PublicKey(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PrecompiledBinaries parse(YamlNode node) {
|
||||||
|
if (node case YamlMap(valueMap: Map<dynamic, YamlNode> map)) {
|
||||||
|
if (map
|
||||||
|
case {
|
||||||
|
'url_prefix': YamlNode urlPrefixNode,
|
||||||
|
'public_key': YamlNode publicKeyNode,
|
||||||
|
}) {
|
||||||
|
final urlPrefix = switch (urlPrefixNode) {
|
||||||
|
YamlScalar(value: String urlPrefix) => urlPrefix,
|
||||||
|
_ => throw SourceSpanException(
|
||||||
|
'Invalid URL prefix value.', urlPrefixNode.span),
|
||||||
|
};
|
||||||
|
final publicKey = switch (publicKeyNode) {
|
||||||
|
YamlScalar(value: String publicKey) =>
|
||||||
|
_publicKeyFromHex(publicKey, publicKeyNode.span),
|
||||||
|
_ => throw SourceSpanException(
|
||||||
|
'Invalid public key value.', publicKeyNode.span),
|
||||||
|
};
|
||||||
|
return PrecompiledBinaries(
|
||||||
|
uriPrefix: urlPrefix,
|
||||||
|
publicKey: publicKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Invalid precompiled binaries value. '
|
||||||
|
'Expected Map with "url_prefix" and "public_key".',
|
||||||
|
node.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cargokit options specified for Rust crate.
|
||||||
|
class CargokitCrateOptions {
|
||||||
|
CargokitCrateOptions({
|
||||||
|
this.cargo = const {},
|
||||||
|
this.precompiledBinaries,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Map<BuildConfiguration, CargoBuildOptions> cargo;
|
||||||
|
final PrecompiledBinaries? precompiledBinaries;
|
||||||
|
|
||||||
|
static CargokitCrateOptions parse(YamlNode node) {
|
||||||
|
if (node is! YamlMap) {
|
||||||
|
throw SourceSpanException('Cargokit options must be a map', node.span);
|
||||||
|
}
|
||||||
|
final options = <BuildConfiguration, CargoBuildOptions>{};
|
||||||
|
PrecompiledBinaries? precompiledBinaries;
|
||||||
|
|
||||||
|
for (final entry in node.nodes.entries) {
|
||||||
|
if (entry
|
||||||
|
case MapEntry(
|
||||||
|
key: YamlScalar(value: 'cargo'),
|
||||||
|
value: YamlNode node,
|
||||||
|
)) {
|
||||||
|
if (node is! YamlMap) {
|
||||||
|
throw SourceSpanException('Cargo options must be a map', node.span);
|
||||||
|
}
|
||||||
|
for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) {
|
||||||
|
if (key case YamlScalar(value: String name)) {
|
||||||
|
final configuration = BuildConfiguration.values
|
||||||
|
.firstWhereOrNull((element) => element.name == name);
|
||||||
|
if (configuration != null) {
|
||||||
|
options[configuration] = CargoBuildOptions.parse(value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.',
|
||||||
|
key.span);
|
||||||
|
}
|
||||||
|
} else if (entry.key case YamlScalar(value: 'precompiled_binaries')) {
|
||||||
|
precompiledBinaries = PrecompiledBinaries.parse(entry.value);
|
||||||
|
} else {
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".',
|
||||||
|
entry.key.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CargokitCrateOptions(
|
||||||
|
cargo: options,
|
||||||
|
precompiledBinaries: precompiledBinaries,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CargokitCrateOptions load({
|
||||||
|
required String manifestDir,
|
||||||
|
}) {
|
||||||
|
final uri = Uri.file(path.join(manifestDir, "cargokit.yaml"));
|
||||||
|
final file = File.fromUri(uri);
|
||||||
|
if (file.existsSync()) {
|
||||||
|
final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri);
|
||||||
|
return parse(contents);
|
||||||
|
} else {
|
||||||
|
return CargokitCrateOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CargokitUserOptions {
|
||||||
|
// When Rustup is installed always build locally unless user opts into
|
||||||
|
// using precompiled binaries.
|
||||||
|
static bool defaultUsePrecompiledBinaries() {
|
||||||
|
return Rustup.executablePath() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CargokitUserOptions({
|
||||||
|
required this.usePrecompiledBinaries,
|
||||||
|
required this.verboseLogging,
|
||||||
|
});
|
||||||
|
|
||||||
|
CargokitUserOptions._()
|
||||||
|
: usePrecompiledBinaries = defaultUsePrecompiledBinaries(),
|
||||||
|
verboseLogging = false;
|
||||||
|
|
||||||
|
static CargokitUserOptions parse(YamlNode node) {
|
||||||
|
if (node is! YamlMap) {
|
||||||
|
throw SourceSpanException('Cargokit options must be a map', node.span);
|
||||||
|
}
|
||||||
|
bool usePrecompiledBinaries = defaultUsePrecompiledBinaries();
|
||||||
|
bool verboseLogging = false;
|
||||||
|
|
||||||
|
for (final entry in node.nodes.entries) {
|
||||||
|
if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) {
|
||||||
|
if (entry.value case YamlScalar(value: bool value)) {
|
||||||
|
usePrecompiledBinaries = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Invalid value for "use_precompiled_binaries". Must be a boolean.',
|
||||||
|
entry.value.span);
|
||||||
|
} else if (entry.key case YamlScalar(value: 'verbose_logging')) {
|
||||||
|
if (entry.value case YamlScalar(value: bool value)) {
|
||||||
|
verboseLogging = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Invalid value for "verbose_logging". Must be a boolean.',
|
||||||
|
entry.value.span);
|
||||||
|
} else {
|
||||||
|
throw SourceSpanException(
|
||||||
|
'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".',
|
||||||
|
entry.key.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CargokitUserOptions(
|
||||||
|
usePrecompiledBinaries: usePrecompiledBinaries,
|
||||||
|
verboseLogging: verboseLogging,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CargokitUserOptions load() {
|
||||||
|
String fileName = "cargokit_options.yaml";
|
||||||
|
var userProjectDir = Directory(Environment.rootProjectDir);
|
||||||
|
|
||||||
|
while (userProjectDir.parent.path != userProjectDir.path) {
|
||||||
|
final configFile = File(path.join(userProjectDir.path, fileName));
|
||||||
|
if (configFile.existsSync()) {
|
||||||
|
final contents = loadYamlNode(
|
||||||
|
configFile.readAsStringSync(),
|
||||||
|
sourceUrl: configFile.uri,
|
||||||
|
);
|
||||||
|
final res = parse(contents);
|
||||||
|
if (res.verboseLogging) {
|
||||||
|
_log.info('Found user options file at ${configFile.path}');
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
userProjectDir = userProjectDir.parent;
|
||||||
|
}
|
||||||
|
return CargokitUserOptions._();
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool usePrecompiledBinaries;
|
||||||
|
final bool verboseLogging;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
import 'package:github/github.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'artifacts_provider.dart';
|
||||||
|
import 'builder.dart';
|
||||||
|
import 'cargo.dart';
|
||||||
|
import 'crate_hash.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'rustup.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
|
||||||
|
final _log = Logger('precompile_binaries');
|
||||||
|
|
||||||
|
class PrecompileBinaries {
|
||||||
|
PrecompileBinaries({
|
||||||
|
required this.privateKey,
|
||||||
|
required this.githubToken,
|
||||||
|
required this.repositorySlug,
|
||||||
|
required this.manifestDir,
|
||||||
|
required this.targets,
|
||||||
|
this.androidSdkLocation,
|
||||||
|
this.androidNdkVersion,
|
||||||
|
this.androidMinSdkVersion,
|
||||||
|
this.tempDir,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PrivateKey privateKey;
|
||||||
|
final String githubToken;
|
||||||
|
final RepositorySlug repositorySlug;
|
||||||
|
final String manifestDir;
|
||||||
|
final List<Target> targets;
|
||||||
|
final String? androidSdkLocation;
|
||||||
|
final String? androidNdkVersion;
|
||||||
|
final int? androidMinSdkVersion;
|
||||||
|
final String? tempDir;
|
||||||
|
|
||||||
|
static String fileName(Target target, String name) {
|
||||||
|
return '${target.rust}_$name';
|
||||||
|
}
|
||||||
|
|
||||||
|
static String signatureFileName(Target target, String name) {
|
||||||
|
return '${target.rust}_$name.sig';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> run() async {
|
||||||
|
final crateInfo = CrateInfo.load(manifestDir);
|
||||||
|
|
||||||
|
final targets = List.of(this.targets);
|
||||||
|
if (targets.isEmpty) {
|
||||||
|
targets.addAll([
|
||||||
|
...Target.buildableTargets(),
|
||||||
|
if (androidSdkLocation != null) ...Target.androidTargets(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.info('Precompiling binaries for $targets');
|
||||||
|
|
||||||
|
final hash = CrateHash.compute(manifestDir);
|
||||||
|
_log.info('Computed crate hash: $hash');
|
||||||
|
|
||||||
|
final String tagName = 'precompiled_$hash';
|
||||||
|
|
||||||
|
final github = GitHub(auth: Authentication.withToken(githubToken));
|
||||||
|
final repo = github.repositories;
|
||||||
|
final release = await _getOrCreateRelease(
|
||||||
|
repo: repo,
|
||||||
|
tagName: tagName,
|
||||||
|
packageName: crateInfo.packageName,
|
||||||
|
hash: hash,
|
||||||
|
);
|
||||||
|
|
||||||
|
final tempDir = this.tempDir != null
|
||||||
|
? Directory(this.tempDir!)
|
||||||
|
: Directory.systemTemp.createTempSync('precompiled_');
|
||||||
|
|
||||||
|
tempDir.createSync(recursive: true);
|
||||||
|
|
||||||
|
final crateOptions = CargokitCrateOptions.load(
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
final buildEnvironment = BuildEnvironment(
|
||||||
|
configuration: BuildConfiguration.release,
|
||||||
|
crateOptions: crateOptions,
|
||||||
|
targetTempDir: tempDir.path,
|
||||||
|
manifestDir: manifestDir,
|
||||||
|
crateInfo: crateInfo,
|
||||||
|
isAndroid: androidSdkLocation != null,
|
||||||
|
androidSdkPath: androidSdkLocation,
|
||||||
|
androidNdkVersion: androidNdkVersion,
|
||||||
|
androidMinSdkVersion: androidMinSdkVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
final rustup = Rustup();
|
||||||
|
|
||||||
|
for (final target in targets) {
|
||||||
|
final artifactNames = getArtifactNames(
|
||||||
|
target: target,
|
||||||
|
libraryName: crateInfo.packageName,
|
||||||
|
remote: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (artifactNames.every((name) {
|
||||||
|
final fileName = PrecompileBinaries.fileName(target, name);
|
||||||
|
return (release.assets ?? []).any((e) => e.name == fileName);
|
||||||
|
})) {
|
||||||
|
_log.info("All artifacts for $target already exist - skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.info('Building for $target');
|
||||||
|
|
||||||
|
final builder =
|
||||||
|
RustBuilder(target: target, environment: buildEnvironment);
|
||||||
|
builder.prepare(rustup);
|
||||||
|
final res = await builder.build();
|
||||||
|
|
||||||
|
final assets = <CreateReleaseAsset>[];
|
||||||
|
for (final name in artifactNames) {
|
||||||
|
final file = File(path.join(res, name));
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
throw Exception('Missing artifact: ${file.path}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = file.readAsBytesSync();
|
||||||
|
final create = CreateReleaseAsset(
|
||||||
|
name: PrecompileBinaries.fileName(target, name),
|
||||||
|
contentType: "application/octet-stream",
|
||||||
|
assetData: data,
|
||||||
|
);
|
||||||
|
final signature = sign(privateKey, data);
|
||||||
|
final signatureCreate = CreateReleaseAsset(
|
||||||
|
name: signatureFileName(target, name),
|
||||||
|
contentType: "application/octet-stream",
|
||||||
|
assetData: signature,
|
||||||
|
);
|
||||||
|
bool verified = verify(public(privateKey), data, signature);
|
||||||
|
if (!verified) {
|
||||||
|
throw Exception('Signature verification failed');
|
||||||
|
}
|
||||||
|
assets.add(create);
|
||||||
|
assets.add(signatureCreate);
|
||||||
|
}
|
||||||
|
_log.info('Uploading assets: ${assets.map((e) => e.name)}');
|
||||||
|
for (final asset in assets) {
|
||||||
|
// This seems to be failing on CI so do it one by one
|
||||||
|
int retryCount = 0;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await repo.uploadReleaseAssets(release, [asset]);
|
||||||
|
break;
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (retryCount == 10) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
++retryCount;
|
||||||
|
_log.shout(
|
||||||
|
'Upload failed (attempt $retryCount, will retry): ${e.toString()}');
|
||||||
|
await Future.delayed(Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.info('Cleaning up');
|
||||||
|
tempDir.deleteSync(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Release> _getOrCreateRelease({
|
||||||
|
required RepositoriesService repo,
|
||||||
|
required String tagName,
|
||||||
|
required String packageName,
|
||||||
|
required String hash,
|
||||||
|
}) async {
|
||||||
|
Release release;
|
||||||
|
try {
|
||||||
|
_log.info('Fetching release $tagName');
|
||||||
|
release = await repo.getReleaseByTagName(repositorySlug, tagName);
|
||||||
|
} on ReleaseNotFound {
|
||||||
|
_log.info('Release not found - creating release $tagName');
|
||||||
|
release = await repo.createRelease(
|
||||||
|
repositorySlug,
|
||||||
|
CreateRelease.from(
|
||||||
|
tagName: tagName,
|
||||||
|
name: 'Precompiled binaries ${hash.substring(0, 8)}',
|
||||||
|
targetCommitish: null,
|
||||||
|
isDraft: false,
|
||||||
|
isPrerelease: false,
|
||||||
|
body: 'Precompiled binaries for crate $packageName, '
|
||||||
|
'crate hash $hash.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
class _Toolchain {
|
||||||
|
_Toolchain(
|
||||||
|
this.name,
|
||||||
|
this.targets,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final List<String> targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rustup {
|
||||||
|
List<String>? installedTargets(String toolchain) {
|
||||||
|
final targets = _installedTargets(toolchain);
|
||||||
|
return targets != null ? List.unmodifiable(targets) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void installToolchain(String toolchain) {
|
||||||
|
log.info("Installing Rust toolchain: $toolchain");
|
||||||
|
runCommand("rustup", ['toolchain', 'install', toolchain]);
|
||||||
|
_installedToolchains
|
||||||
|
.add(_Toolchain(toolchain, _getInstalledTargets(toolchain)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void installTarget(
|
||||||
|
String target, {
|
||||||
|
required String toolchain,
|
||||||
|
}) {
|
||||||
|
log.info("Installing Rust target: $target");
|
||||||
|
runCommand("rustup", [
|
||||||
|
'target',
|
||||||
|
'add',
|
||||||
|
'--toolchain',
|
||||||
|
toolchain,
|
||||||
|
target,
|
||||||
|
]);
|
||||||
|
_installedTargets(toolchain)?.add(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<_Toolchain> _installedToolchains;
|
||||||
|
|
||||||
|
Rustup() : _installedToolchains = _getInstalledToolchains();
|
||||||
|
|
||||||
|
List<String>? _installedTargets(String toolchain) => _installedToolchains
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(e) => e.name == toolchain || e.name.startsWith('$toolchain-'))
|
||||||
|
?.targets;
|
||||||
|
|
||||||
|
static List<_Toolchain> _getInstalledToolchains() {
|
||||||
|
String extractToolchainName(String line) {
|
||||||
|
// ignore (default) after toolchain name
|
||||||
|
final parts = line.split(' ');
|
||||||
|
return parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
final res = runCommand("rustup", ['toolchain', 'list']);
|
||||||
|
|
||||||
|
// To list all non-custom toolchains, we need to filter out lines that
|
||||||
|
// don't start with "stable", "beta", or "nightly".
|
||||||
|
Pattern nonCustom = RegExp(r"^(stable|beta|nightly)");
|
||||||
|
final lines = res.stdout
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.where((e) => e.isNotEmpty && e.startsWith(nonCustom))
|
||||||
|
.map(extractToolchainName)
|
||||||
|
.toList(growable: true);
|
||||||
|
|
||||||
|
return lines
|
||||||
|
.map(
|
||||||
|
(name) => _Toolchain(
|
||||||
|
name,
|
||||||
|
_getInstalledTargets(name),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(growable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<String> _getInstalledTargets(String toolchain) {
|
||||||
|
final res = runCommand("rustup", [
|
||||||
|
'target',
|
||||||
|
'list',
|
||||||
|
'--toolchain',
|
||||||
|
toolchain,
|
||||||
|
'--installed',
|
||||||
|
]);
|
||||||
|
final lines = res.stdout
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.where((e) => e.isNotEmpty)
|
||||||
|
.toList(growable: true);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _didInstallRustSrcForNightly = false;
|
||||||
|
|
||||||
|
void installRustSrcForNightly() {
|
||||||
|
if (_didInstallRustSrcForNightly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Useful for -Z build-std
|
||||||
|
runCommand(
|
||||||
|
"rustup",
|
||||||
|
['component', 'add', 'rust-src', '--toolchain', 'nightly'],
|
||||||
|
);
|
||||||
|
_didInstallRustSrcForNightly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? executablePath() {
|
||||||
|
final envPath = Platform.environment['PATH'];
|
||||||
|
final envPathSeparator = Platform.isWindows ? ';' : ':';
|
||||||
|
final home = Platform.isWindows
|
||||||
|
? Platform.environment['USERPROFILE']
|
||||||
|
: Platform.environment['HOME'];
|
||||||
|
final paths = [
|
||||||
|
if (home != null) path.join(home, '.cargo', 'bin'),
|
||||||
|
if (envPath != null) ...envPath.split(envPathSeparator),
|
||||||
|
];
|
||||||
|
for (final p in paths) {
|
||||||
|
final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup';
|
||||||
|
final rustupPath = path.join(p, rustup);
|
||||||
|
if (File(rustupPath).existsSync()) {
|
||||||
|
return rustupPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
class Target {
|
||||||
|
Target({
|
||||||
|
required this.rust,
|
||||||
|
this.flutter,
|
||||||
|
this.android,
|
||||||
|
this.androidMinSdkVersion,
|
||||||
|
this.darwinPlatform,
|
||||||
|
this.darwinArch,
|
||||||
|
});
|
||||||
|
|
||||||
|
static final all = [
|
||||||
|
Target(
|
||||||
|
rust: 'armv7-linux-androideabi',
|
||||||
|
flutter: 'android-arm',
|
||||||
|
android: 'armeabi-v7a',
|
||||||
|
androidMinSdkVersion: 16,
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'aarch64-linux-android',
|
||||||
|
flutter: 'android-arm64',
|
||||||
|
android: 'arm64-v8a',
|
||||||
|
androidMinSdkVersion: 21,
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'i686-linux-android',
|
||||||
|
flutter: 'android-x86',
|
||||||
|
android: 'x86',
|
||||||
|
androidMinSdkVersion: 16,
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'x86_64-linux-android',
|
||||||
|
flutter: 'android-x64',
|
||||||
|
android: 'x86_64',
|
||||||
|
androidMinSdkVersion: 21,
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'x86_64-pc-windows-msvc',
|
||||||
|
flutter: 'windows-x64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'x86_64-unknown-linux-gnu',
|
||||||
|
flutter: 'linux-x64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'aarch64-unknown-linux-gnu',
|
||||||
|
flutter: 'linux-arm64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'x86_64-apple-darwin',
|
||||||
|
darwinPlatform: 'macosx',
|
||||||
|
darwinArch: 'x86_64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'aarch64-apple-darwin',
|
||||||
|
darwinPlatform: 'macosx',
|
||||||
|
darwinArch: 'arm64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'aarch64-apple-ios',
|
||||||
|
darwinPlatform: 'iphoneos',
|
||||||
|
darwinArch: 'arm64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'aarch64-apple-ios-sim',
|
||||||
|
darwinPlatform: 'iphonesimulator',
|
||||||
|
darwinArch: 'arm64',
|
||||||
|
),
|
||||||
|
Target(
|
||||||
|
rust: 'x86_64-apple-ios',
|
||||||
|
darwinPlatform: 'iphonesimulator',
|
||||||
|
darwinArch: 'x86_64',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
static Target? forFlutterName(String flutterName) {
|
||||||
|
return all.firstWhereOrNull((element) => element.flutter == flutterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Target? forDarwin({
|
||||||
|
required String platformName,
|
||||||
|
required String darwinAarch,
|
||||||
|
}) {
|
||||||
|
return all.firstWhereOrNull((element) => //
|
||||||
|
element.darwinPlatform == platformName &&
|
||||||
|
element.darwinArch == darwinAarch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Target? forRustTriple(String triple) {
|
||||||
|
return all.firstWhereOrNull((element) => element.rust == triple);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Target> androidTargets() {
|
||||||
|
return all
|
||||||
|
.where((element) => element.android != null)
|
||||||
|
.toList(growable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns buildable targets on current host platform ignoring Android targets.
|
||||||
|
static List<Target> buildableTargets() {
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
// Right now we don't support cross-compiling on Linux. So we just return
|
||||||
|
// the host target.
|
||||||
|
final arch = runCommand('arch', []).stdout as String;
|
||||||
|
if (arch.trim() == 'aarch64') {
|
||||||
|
return [Target.forRustTriple('aarch64-unknown-linux-gnu')!];
|
||||||
|
} else {
|
||||||
|
return [Target.forRustTriple('x86_64-unknown-linux-gnu')!];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all.where((target) {
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
return target.rust.contains('-windows-');
|
||||||
|
} else if (Platform.isMacOS) {
|
||||||
|
return target.darwinPlatform != null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}).toList(growable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return rust;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String? flutter;
|
||||||
|
final String rust;
|
||||||
|
final String? android;
|
||||||
|
final int? androidMinSdkVersion;
|
||||||
|
final String? darwinPlatform;
|
||||||
|
final String? darwinArch;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import 'logging.dart';
|
||||||
|
import 'rustup.dart';
|
||||||
|
|
||||||
|
final log = Logger("process");
|
||||||
|
|
||||||
|
class CommandFailedException implements Exception {
|
||||||
|
final String executable;
|
||||||
|
final List<String> arguments;
|
||||||
|
final ProcessResult result;
|
||||||
|
|
||||||
|
CommandFailedException({
|
||||||
|
required this.executable,
|
||||||
|
required this.arguments,
|
||||||
|
required this.result,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
final stdout = result.stdout.toString().trim();
|
||||||
|
final stderr = result.stderr.toString().trim();
|
||||||
|
return [
|
||||||
|
"External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}",
|
||||||
|
"Returned Exit Code: ${result.exitCode}",
|
||||||
|
kSeparator,
|
||||||
|
"STDOUT:",
|
||||||
|
if (stdout.isNotEmpty) stdout,
|
||||||
|
kSeparator,
|
||||||
|
"STDERR:",
|
||||||
|
if (stderr.isNotEmpty) stderr,
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRunCommandArgs {
|
||||||
|
final String executable;
|
||||||
|
final List<String> arguments;
|
||||||
|
final String? workingDirectory;
|
||||||
|
final Map<String, String>? environment;
|
||||||
|
final bool includeParentEnvironment;
|
||||||
|
final bool runInShell;
|
||||||
|
final Encoding? stdoutEncoding;
|
||||||
|
final Encoding? stderrEncoding;
|
||||||
|
|
||||||
|
TestRunCommandArgs({
|
||||||
|
required this.executable,
|
||||||
|
required this.arguments,
|
||||||
|
this.workingDirectory,
|
||||||
|
this.environment,
|
||||||
|
this.includeParentEnvironment = true,
|
||||||
|
this.runInShell = false,
|
||||||
|
this.stdoutEncoding,
|
||||||
|
this.stderrEncoding,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRunCommandResult {
|
||||||
|
TestRunCommandResult({
|
||||||
|
this.pid = 1,
|
||||||
|
this.exitCode = 0,
|
||||||
|
this.stdout = '',
|
||||||
|
this.stderr = '',
|
||||||
|
});
|
||||||
|
|
||||||
|
final int pid;
|
||||||
|
final int exitCode;
|
||||||
|
final String stdout;
|
||||||
|
final String stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride;
|
||||||
|
|
||||||
|
ProcessResult runCommand(
|
||||||
|
String executable,
|
||||||
|
List<String> arguments, {
|
||||||
|
String? workingDirectory,
|
||||||
|
Map<String, String>? environment,
|
||||||
|
bool includeParentEnvironment = true,
|
||||||
|
bool runInShell = false,
|
||||||
|
Encoding? stdoutEncoding = systemEncoding,
|
||||||
|
Encoding? stderrEncoding = systemEncoding,
|
||||||
|
}) {
|
||||||
|
if (testRunCommandOverride != null) {
|
||||||
|
final result = testRunCommandOverride!(TestRunCommandArgs(
|
||||||
|
executable: executable,
|
||||||
|
arguments: arguments,
|
||||||
|
workingDirectory: workingDirectory,
|
||||||
|
environment: environment,
|
||||||
|
includeParentEnvironment: includeParentEnvironment,
|
||||||
|
runInShell: runInShell,
|
||||||
|
stdoutEncoding: stdoutEncoding,
|
||||||
|
stderrEncoding: stderrEncoding,
|
||||||
|
));
|
||||||
|
return ProcessResult(
|
||||||
|
result.pid,
|
||||||
|
result.exitCode,
|
||||||
|
result.stdout,
|
||||||
|
result.stderr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
log.finer('Running command $executable ${arguments.join(' ')}');
|
||||||
|
final res = Process.runSync(
|
||||||
|
_resolveExecutable(executable),
|
||||||
|
arguments,
|
||||||
|
workingDirectory: workingDirectory,
|
||||||
|
environment: environment,
|
||||||
|
includeParentEnvironment: includeParentEnvironment,
|
||||||
|
runInShell: runInShell,
|
||||||
|
stderrEncoding: stderrEncoding,
|
||||||
|
stdoutEncoding: stdoutEncoding,
|
||||||
|
);
|
||||||
|
if (res.exitCode != 0) {
|
||||||
|
throw CommandFailedException(
|
||||||
|
executable: executable,
|
||||||
|
arguments: arguments,
|
||||||
|
result: res,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RustupNotFoundException implements Exception {
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return [
|
||||||
|
' ',
|
||||||
|
'rustup not found in PATH.',
|
||||||
|
' ',
|
||||||
|
'Maybe you need to install Rust? It only takes a minute:',
|
||||||
|
' ',
|
||||||
|
if (Platform.isWindows) 'https://www.rust-lang.org/tools/install',
|
||||||
|
if (hasHomebrewRustInPath()) ...[
|
||||||
|
'\$ brew unlink rust # Unlink homebrew Rust from PATH',
|
||||||
|
],
|
||||||
|
if (!Platform.isWindows)
|
||||||
|
"\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh",
|
||||||
|
' ',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasHomebrewRustInPath() {
|
||||||
|
if (!Platform.isMacOS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final envPath = Platform.environment['PATH'] ?? '';
|
||||||
|
final paths = envPath.split(':');
|
||||||
|
return paths.any((p) {
|
||||||
|
return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _resolveExecutable(String executable) {
|
||||||
|
if (executable == 'rustup') {
|
||||||
|
final resolved = Rustup.executablePath();
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
throw RustupNotFoundException();
|
||||||
|
} else {
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
|
import 'artifacts_provider.dart';
|
||||||
|
import 'cargo.dart';
|
||||||
|
import 'crate_hash.dart';
|
||||||
|
import 'options.dart';
|
||||||
|
import 'precompile_binaries.dart';
|
||||||
|
import 'target.dart';
|
||||||
|
|
||||||
|
class VerifyBinaries {
|
||||||
|
VerifyBinaries({
|
||||||
|
required this.manifestDir,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String manifestDir;
|
||||||
|
|
||||||
|
Future<void> run() async {
|
||||||
|
final crateInfo = CrateInfo.load(manifestDir);
|
||||||
|
|
||||||
|
final config = CargokitCrateOptions.load(manifestDir: manifestDir);
|
||||||
|
final precompiledBinaries = config.precompiledBinaries;
|
||||||
|
if (precompiledBinaries == null) {
|
||||||
|
stdout.writeln('Crate does not support precompiled binaries.');
|
||||||
|
} else {
|
||||||
|
final crateHash = CrateHash.compute(manifestDir);
|
||||||
|
stdout.writeln('Crate hash: $crateHash');
|
||||||
|
|
||||||
|
for (final target in Target.all) {
|
||||||
|
final message = 'Checking ${target.rust}...';
|
||||||
|
stdout.write(message.padRight(40));
|
||||||
|
stdout.flush();
|
||||||
|
|
||||||
|
final artifacts = getArtifactNames(
|
||||||
|
target: target,
|
||||||
|
libraryName: crateInfo.packageName,
|
||||||
|
remote: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
final prefix = precompiledBinaries.uriPrefix;
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
for (final artifact in artifacts) {
|
||||||
|
final fileName = PrecompileBinaries.fileName(target, artifact);
|
||||||
|
final signatureFileName =
|
||||||
|
PrecompileBinaries.signatureFileName(target, artifact);
|
||||||
|
|
||||||
|
final url = Uri.parse('$prefix$crateHash/$fileName');
|
||||||
|
final signatureUrl =
|
||||||
|
Uri.parse('$prefix$crateHash/$signatureFileName');
|
||||||
|
|
||||||
|
final signature = await get(signatureUrl);
|
||||||
|
if (signature.statusCode != 200) {
|
||||||
|
stdout.writeln('MISSING');
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final asset = await get(url);
|
||||||
|
if (asset.statusCode != 200) {
|
||||||
|
stdout.writeln('MISSING');
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verify(precompiledBinaries.publicKey, asset.bodyBytes,
|
||||||
|
signature.bodyBytes)) {
|
||||||
|
stdout.writeln('INVALID SIGNATURE');
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
stdout.writeln('OK');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,453 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "64.0.0"
|
||||||
|
adaptive_number:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: adaptive_number
|
||||||
|
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.0"
|
||||||
|
args:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.11.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
collection:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.18.0"
|
||||||
|
convert:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
coverage:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: coverage
|
||||||
|
sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.3"
|
||||||
|
crypto:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
ed25519_edwards:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: ed25519_edwards
|
||||||
|
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.4"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
|
github:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: github
|
||||||
|
sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.17.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
hex:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hex
|
||||||
|
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.8.1"
|
||||||
|
lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
logging:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.16"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
node_preamble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: node_preamble
|
||||||
|
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
path:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.4.0"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
shelf_packages_handler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_packages_handler
|
||||||
|
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
shelf_static:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_static
|
||||||
|
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
|
source_map_stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_map_stack_trace
|
||||||
|
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
source_maps:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_maps
|
||||||
|
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.12"
|
||||||
|
source_span:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.0"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.1"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: test
|
||||||
|
sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.24.6"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.1"
|
||||||
|
test_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_core
|
||||||
|
sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.6"
|
||||||
|
toml:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: toml
|
||||||
|
sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.0"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.2"
|
||||||
|
version:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: version
|
||||||
|
sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.9.0"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
webkit_inspection_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webkit_inspection_protocol
|
||||||
|
sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
yaml:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.0.0 <4.0.0"
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
name: build_tool
|
||||||
|
description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build.
|
||||||
|
publish_to: none
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|
||||||
|
# Add regular dependencies here.
|
||||||
|
dependencies:
|
||||||
|
# these are pinned on purpose because the bundle_tool_runner doesn't have
|
||||||
|
# pubspec.lock. See run_build_tool.sh
|
||||||
|
logging: 1.2.0
|
||||||
|
path: 1.8.0
|
||||||
|
version: 3.0.0
|
||||||
|
collection: 1.18.0
|
||||||
|
ed25519_edwards: 0.3.1
|
||||||
|
hex: 0.2.0
|
||||||
|
yaml: 3.1.2
|
||||||
|
source_span: 1.10.0
|
||||||
|
github: 9.17.0
|
||||||
|
args: 2.4.2
|
||||||
|
crypto: 3.0.3
|
||||||
|
convert: 3.1.1
|
||||||
|
http: 1.1.0
|
||||||
|
toml: 0.14.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
lints: ^2.1.0
|
||||||
|
test: ^1.24.0
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..")
|
||||||
|
|
||||||
|
# Workaround for https://github.com/dart-lang/pub/issues/4010
|
||||||
|
get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# REALPATH does not properly resolve symlinks on windows :-/
|
||||||
|
execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
# - target: CMAKE target to which rust library is linked
|
||||||
|
# - manifest_dir: relative path from current folder to directory containing cargo manifest
|
||||||
|
# - lib_name: cargo package name
|
||||||
|
# - any_symbol_name: name of any exported symbol from the library.
|
||||||
|
# used on windows to force linking with library.
|
||||||
|
function(apply_cargokit target manifest_dir lib_name any_symbol_name)
|
||||||
|
|
||||||
|
set(CARGOKIT_LIB_NAME "${lib_name}")
|
||||||
|
set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}")
|
||||||
|
if (CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
|
||||||
|
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${CARGOKIT_LIB_FULL_NAME}")
|
||||||
|
else()
|
||||||
|
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}")
|
||||||
|
endif()
|
||||||
|
set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build")
|
||||||
|
|
||||||
|
if (FLUTTER_TARGET_PLATFORM)
|
||||||
|
set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}")
|
||||||
|
else()
|
||||||
|
set(CARGOKIT_TARGET_PLATFORM "windows-x64")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CARGOKIT_ENV
|
||||||
|
"CARGOKIT_CMAKE=${CMAKE_COMMAND}"
|
||||||
|
"CARGOKIT_CONFIGURATION=$<CONFIG>"
|
||||||
|
"CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}"
|
||||||
|
"CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}"
|
||||||
|
"CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}"
|
||||||
|
"CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}"
|
||||||
|
"CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool"
|
||||||
|
"CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(SCRIPT_EXTENSION ".cmd")
|
||||||
|
set(IMPORT_LIB_EXTENSION ".lib")
|
||||||
|
else()
|
||||||
|
set(SCRIPT_EXTENSION ".sh")
|
||||||
|
set(IMPORT_LIB_EXTENSION "")
|
||||||
|
execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Using generators in custom command is only supported in CMake 3.20+
|
||||||
|
if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0")
|
||||||
|
foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
|
||||||
|
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
${OUTPUT_LIB}
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
|
||||||
|
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE)
|
||||||
|
|
||||||
|
if (TARGET ${target})
|
||||||
|
# If we have actual cmake target provided create target and make existing
|
||||||
|
# target depend on it
|
||||||
|
add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB})
|
||||||
|
add_dependencies("${target}" "${target}_cargokit")
|
||||||
|
target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}")
|
||||||
|
if(WIN32)
|
||||||
|
target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Otherwise (FFI) just use ALL to force building always
|
||||||
|
add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Allow adding the output library to plugin bundled libraries
|
||||||
|
set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE)
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
function Resolve-Symlinks {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param(
|
||||||
|
[Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||||
|
[string] $Path
|
||||||
|
)
|
||||||
|
|
||||||
|
[string] $separator = '/'
|
||||||
|
[string[]] $parts = $Path.Split($separator)
|
||||||
|
|
||||||
|
[string] $realPath = ''
|
||||||
|
foreach ($part in $parts) {
|
||||||
|
if ($realPath -and !$realPath.EndsWith($separator)) {
|
||||||
|
$realPath += $separator
|
||||||
|
}
|
||||||
|
|
||||||
|
$realPath += $part.Replace('\', '/')
|
||||||
|
|
||||||
|
# The slash is important when using Get-Item on Drive letters in pwsh.
|
||||||
|
if (-not($realPath.Contains($separator)) -and $realPath.EndsWith(':')) {
|
||||||
|
$realPath += '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = Get-Item $realPath
|
||||||
|
if ($item.LinkTarget) {
|
||||||
|
$realPath = $item.LinkTarget.Replace('\', '/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$realPath
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = Resolve-Symlinks -Path $args[0]
|
||||||
|
Write-Host $path
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||||
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||||
|
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
|
||||||
|
CargoKitPlugin.file = buildscript.sourceFile
|
||||||
|
|
||||||
|
apply plugin: CargoKitPlugin
|
||||||
|
|
||||||
|
class CargoKitExtension {
|
||||||
|
String manifestDir; // Relative path to folder containing Cargo.toml
|
||||||
|
String libname; // Library name within Cargo.toml. Must be a cdylib
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CargoKitBuildTask extends DefaultTask {
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String buildMode
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String buildDir
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String outputDir
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String ndkVersion
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String sdkDirectory
|
||||||
|
|
||||||
|
@Input
|
||||||
|
int compileSdkVersion;
|
||||||
|
|
||||||
|
@Input
|
||||||
|
int minSdkVersion;
|
||||||
|
|
||||||
|
@Input
|
||||||
|
String pluginFile
|
||||||
|
|
||||||
|
@Input
|
||||||
|
List<String> targetPlatforms
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
def build() {
|
||||||
|
if (project.cargokit.manifestDir == null) {
|
||||||
|
throw new GradleException("Property 'manifestDir' must be set on cargokit extension");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.cargokit.libname == null) {
|
||||||
|
throw new GradleException("Property 'libname' must be set on cargokit extension");
|
||||||
|
}
|
||||||
|
|
||||||
|
def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh"
|
||||||
|
def path = Paths.get(new File(pluginFile).parent, "..", executableName);
|
||||||
|
|
||||||
|
def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir)
|
||||||
|
|
||||||
|
def rootProjectDir = project.rootProject.projectDir
|
||||||
|
|
||||||
|
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
project.exec {
|
||||||
|
commandLine 'chmod', '+x', path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.exec {
|
||||||
|
executable path
|
||||||
|
args "build-gradle"
|
||||||
|
environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir
|
||||||
|
environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool"
|
||||||
|
environment "CARGOKIT_MANIFEST_DIR", manifestDir
|
||||||
|
environment "CARGOKIT_CONFIGURATION", buildMode
|
||||||
|
environment "CARGOKIT_TARGET_TEMP_DIR", buildDir
|
||||||
|
environment "CARGOKIT_OUTPUT_DIR", outputDir
|
||||||
|
environment "CARGOKIT_NDK_VERSION", ndkVersion
|
||||||
|
environment "CARGOKIT_SDK_DIR", sdkDirectory
|
||||||
|
environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion
|
||||||
|
environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion
|
||||||
|
environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",")
|
||||||
|
environment "CARGOKIT_JAVA_HOME", System.properties['java.home']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CargoKitPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
static String file;
|
||||||
|
|
||||||
|
private Plugin findFlutterPlugin(Project rootProject) {
|
||||||
|
_findFlutterPlugin(rootProject.childProjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plugin _findFlutterPlugin(Map projects) {
|
||||||
|
for (project in projects) {
|
||||||
|
for (plugin in project.value.getPlugins()) {
|
||||||
|
if (plugin.class.name == "com.flutter.gradle.FlutterPlugin") {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def plugin = _findFlutterPlugin(project.value.childProjects);
|
||||||
|
if (plugin != null) {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(Project project) {
|
||||||
|
def plugin = findFlutterPlugin(project.rootProject);
|
||||||
|
|
||||||
|
project.extensions.create("cargokit", CargoKitExtension)
|
||||||
|
|
||||||
|
if (plugin == null) {
|
||||||
|
print("Flutter plugin not found, CargoKit plugin will not be applied.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
def cargoBuildDir = "${project.buildDir}/build"
|
||||||
|
|
||||||
|
// Determine if the project is an application or library
|
||||||
|
def isApplication = plugin.project.plugins.hasPlugin('com.android.application')
|
||||||
|
def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants
|
||||||
|
|
||||||
|
variants.all { variant ->
|
||||||
|
|
||||||
|
final buildType = variant.buildType.name
|
||||||
|
|
||||||
|
def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}";
|
||||||
|
def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs;
|
||||||
|
jniLibs.srcDir(new File(cargoOutputDir))
|
||||||
|
|
||||||
|
def platforms = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).collect()
|
||||||
|
|
||||||
|
// Same thing addFlutterDependencies does in flutter.gradle
|
||||||
|
if (buildType == "debug") {
|
||||||
|
platforms.add("android-x86")
|
||||||
|
platforms.add("android-x64")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The task name depends on plugin properties, which are not available
|
||||||
|
// at this point
|
||||||
|
project.getGradle().afterProject {
|
||||||
|
def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}";
|
||||||
|
|
||||||
|
if (project.tasks.findByName(taskName)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugin.project.android.ndkVersion == null) {
|
||||||
|
throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def task = project.tasks.create(taskName, CargoKitBuildTask.class) {
|
||||||
|
buildMode = variant.buildType.name
|
||||||
|
buildDir = cargoBuildDir
|
||||||
|
outputDir = cargoOutputDir
|
||||||
|
ndkVersion = plugin.project.android.ndkVersion
|
||||||
|
sdkDirectory = plugin.project.android.sdkDirectory
|
||||||
|
minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int
|
||||||
|
compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int
|
||||||
|
targetPlatforms = platforms
|
||||||
|
pluginFile = CargoKitPlugin.file
|
||||||
|
}
|
||||||
|
def onTask = { newTask ->
|
||||||
|
if (newTask.name == "merge${buildType.capitalize()}NativeLibs") {
|
||||||
|
newTask.dependsOn task
|
||||||
|
// Fix gradle 7.4.2 not picking up JNI library changes
|
||||||
|
newTask.outputs.upToDateWhen { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.tasks.each onTask
|
||||||
|
project.tasks.whenTaskAdded onTask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
|
||||||
|
SET BASEDIR=%~dp0
|
||||||
|
|
||||||
|
if not exist "%CARGOKIT_TOOL_TEMP_DIR%" (
|
||||||
|
mkdir "%CARGOKIT_TOOL_TEMP_DIR%"
|
||||||
|
)
|
||||||
|
cd /D "%CARGOKIT_TOOL_TEMP_DIR%"
|
||||||
|
|
||||||
|
SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool
|
||||||
|
SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart
|
||||||
|
|
||||||
|
set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/%
|
||||||
|
|
||||||
|
(
|
||||||
|
echo name: build_tool_runner
|
||||||
|
echo version: 1.0.0
|
||||||
|
echo publish_to: none
|
||||||
|
echo.
|
||||||
|
echo environment:
|
||||||
|
echo sdk: '^>=3.0.0 ^<4.0.0'
|
||||||
|
echo.
|
||||||
|
echo dependencies:
|
||||||
|
echo build_tool:
|
||||||
|
echo path: %BUILD_TOOL_PKG_DIR_POSIX%
|
||||||
|
) >pubspec.yaml
|
||||||
|
|
||||||
|
if not exist bin (
|
||||||
|
mkdir bin
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
echo import 'package:build_tool/build_tool.dart' as build_tool;
|
||||||
|
echo void main^(List^<String^> args^) ^{
|
||||||
|
echo build_tool.runMain^(args^);
|
||||||
|
echo ^}
|
||||||
|
) >bin\build_tool_runner.dart
|
||||||
|
|
||||||
|
SET PRECOMPILED=bin\build_tool_runner.dill
|
||||||
|
|
||||||
|
REM To detect changes in package we compare output of DIR /s (recursive)
|
||||||
|
set PREV_PACKAGE_INFO=.dart_tool\package_info.prev
|
||||||
|
set CUR_PACKAGE_INFO=.dart_tool\package_info.cur
|
||||||
|
|
||||||
|
DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig"
|
||||||
|
|
||||||
|
REM Last line in dir output is free space on harddrive. That is bound to
|
||||||
|
REM change between invocation so we need to remove it
|
||||||
|
(
|
||||||
|
Set "Line="
|
||||||
|
For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do (
|
||||||
|
SetLocal EnableDelayedExpansion
|
||||||
|
If Defined Line Echo !Line!
|
||||||
|
EndLocal
|
||||||
|
Set "Line=%%A")
|
||||||
|
) >"%CUR_PACKAGE_INFO%"
|
||||||
|
DEL "%CUR_PACKAGE_INFO%_orig"
|
||||||
|
|
||||||
|
REM Compare current directory listing with previous
|
||||||
|
FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1
|
||||||
|
|
||||||
|
If %ERRORLEVEL% neq 0 (
|
||||||
|
REM Changed - copy current to previous and remove precompiled kernel
|
||||||
|
if exist "%PREV_PACKAGE_INFO%" (
|
||||||
|
DEL "%PREV_PACKAGE_INFO%"
|
||||||
|
)
|
||||||
|
MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%"
|
||||||
|
if exist "%PRECOMPILED%" (
|
||||||
|
DEL "%PRECOMPILED%"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO%
|
||||||
|
REM which means we need to do pub get and precompile
|
||||||
|
if not exist "%PRECOMPILED%" (
|
||||||
|
echo Running pub get in "%cd%"
|
||||||
|
"%DART%" pub get --no-precompile
|
||||||
|
"%DART%" compile kernel bin/build_tool_runner.dart
|
||||||
|
)
|
||||||
|
|
||||||
|
"%DART%" "%PRECOMPILED%" %*
|
||||||
|
|
||||||
|
REM 253 means invalid snapshot version.
|
||||||
|
If %ERRORLEVEL% equ 253 (
|
||||||
|
"%DART%" pub get --no-precompile
|
||||||
|
"%DART%" compile kernel bin/build_tool_runner.dart
|
||||||
|
"%DART%" "%PRECOMPILED%" %*
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASEDIR=$(dirname "$0")
|
||||||
|
|
||||||
|
mkdir -p "$CARGOKIT_TOOL_TEMP_DIR"
|
||||||
|
|
||||||
|
cd "$CARGOKIT_TOOL_TEMP_DIR"
|
||||||
|
|
||||||
|
# Write a very simple bin package in temp folder that depends on build_tool package
|
||||||
|
# from Cargokit. This is done to ensure that we don't pollute Cargokit folder
|
||||||
|
# with .dart_tool contents.
|
||||||
|
|
||||||
|
BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool"
|
||||||
|
|
||||||
|
if [[ -z $FLUTTER_ROOT ]]; then # not defined
|
||||||
|
DART=dart
|
||||||
|
else
|
||||||
|
DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat << EOF > "pubspec.yaml"
|
||||||
|
name: build_tool_runner
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
build_tool:
|
||||||
|
path: "$BUILD_TOOL_PKG_DIR"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mkdir -p "bin"
|
||||||
|
|
||||||
|
cat << EOF > "bin/build_tool_runner.dart"
|
||||||
|
import 'package:build_tool/build_tool.dart' as build_tool;
|
||||||
|
void main(List<String> args) {
|
||||||
|
build_tool.runMain(args);
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create alias for `shasum` if it does not exist and `sha1sum` exists
|
||||||
|
if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then
|
||||||
|
shopt -s expand_aliases
|
||||||
|
alias shasum="sha1sum"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dart run will not cache any package that has a path dependency, which
|
||||||
|
# is the case for our build_tool_runner. So instead we precompile the package
|
||||||
|
# ourselves.
|
||||||
|
# To invalidate the cached kernel we use the hash of ls -LR of the build_tool
|
||||||
|
# package directory. This should be good enough, as the build_tool package
|
||||||
|
# itself is not meant to have any path dependencies.
|
||||||
|
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum)
|
||||||
|
else
|
||||||
|
PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum)
|
||||||
|
fi
|
||||||
|
|
||||||
|
PACKAGE_HASH_FILE=".package_hash"
|
||||||
|
|
||||||
|
if [ -f "$PACKAGE_HASH_FILE" ]; then
|
||||||
|
EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE")
|
||||||
|
if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then
|
||||||
|
rm "$PACKAGE_HASH_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run pub get if needed.
|
||||||
|
if [ ! -f "$PACKAGE_HASH_FILE" ]; then
|
||||||
|
"$DART" pub get --no-precompile
|
||||||
|
"$DART" compile kernel bin/build_tool_runner.dart
|
||||||
|
echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rebuild the tool if it was deleted by Android Studio
|
||||||
|
if [ ! -f "bin/build_tool_runner.dill" ]; then
|
||||||
|
"$DART" compile kernel bin/build_tool_runner.dart
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
"$DART" bin/build_tool_runner.dill "$@"
|
||||||
|
|
||||||
|
exit_code=$?
|
||||||
|
|
||||||
|
# 253 means invalid snapshot version.
|
||||||
|
if [ $exit_code == 253 ]; then
|
||||||
|
"$DART" pub get --no-precompile
|
||||||
|
"$DART" compile kernel bin/build_tool_runner.dart
|
||||||
|
"$DART" bin/build_tool_runner.dill "$@"
|
||||||
|
exit_code=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $exit_code
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FRB v2
|
||||||
|
======
|
||||||
|
With flutter_rust_bridge, the Rust dependency lives in Cargo.toml, not pubspec.yaml
|
||||||
|
|
||||||
|
FRB v2 ships with Cargokit — a build helper that hooks into each platform's build
|
||||||
|
system (Gradle, Xcode, CMake) and compiles the Rust crate automatically at flutter build time.
|
||||||
|
This means:
|
||||||
|
* ccc_rust does not need a pubspec.yaml
|
||||||
|
* ccc_rust stays a pure Rust crate
|
||||||
|
* The current lum_ccc_rust entry in pubspec.yaml would be removed — it moves to rust/Cargo.toml
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
ccc_dart_plugin/
|
||||||
|
├── pubspec.yaml ← NO reference to ccc_rust here
|
||||||
|
├── rust/
|
||||||
|
│ ├── Cargo.toml ← ccc_rust referenced HERE as a Rust crate dep
|
||||||
|
│ └── src/api.rs ← thin wrappers calling ccc_rust
|
||||||
|
|
||||||
|
|
||||||
|
ccc_rust
|
||||||
|
========
|
||||||
|
The Rust crypto library remains a pure Rust crate with no Flutter or FFI-specific code.
|
||||||
|
Option 1: Build from source via Cargokit (recommended for development)
|
||||||
|
* The Rust crate references ccc_rust via a git tag in Cargo.toml
|
||||||
|
* Cargokit compiles the Rust code at build time for each platform
|
||||||
|
* No precompiled binaries are needed
|
||||||
|
|
||||||
|
Option 2: Cargokit precompiled binaries via GitHub Releases (recommended for pub.dev)
|
||||||
|
* The Rust crate is compiled into platform-specific binaries (staticlib for iOS/macOS, cdylib for Android)
|
||||||
|
* Binaries are uploaded to GitHub Releases
|
||||||
|
* Cargokit fetches the correct binary at build time based on the target platform
|
||||||
|
* This allows pub.dev users to get native performance without needing Rust or a build environment
|
||||||
|
|
||||||
|
|
||||||
|
Workflow
|
||||||
|
--------
|
||||||
|
* CI builds the Rust crate for all 5 platform targets
|
||||||
|
* CI uploads the .so/.dylib/.dll/.framework to a GitHub Release tagged with the Cargo package hash
|
||||||
|
* When consumers run flutter build, Cargokit downloads the precompiled binary instead of compiling
|
||||||
|
* If no precompiled binary exists, it falls back to source build
|
||||||
|
* Pros: Fast builds for consumers, no Rust toolchain needed, no repo bloat, built into FRB, single repo
|
||||||
|
* Cons: Requires CI pipeline to build + upload binaries
|
||||||
|
* Best for: pub.dev releases, distributing to teams without Rust installed
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
CCC Dart Plugin — Architecture & Integration Guide
|
CCC Dart Plugin — Architecture & Integration Guide
|
||||||
====================================================
|
====================================================
|
||||||
|
|
||||||
:Status: Milestone 2 (Planned)
|
:Status: Milestone 2 (In Progress)
|
||||||
:Depends on: ``ccc_rust`` ≥ v0.1.0
|
:Depends on: ``ccc_rust`` ≥ v0.1.0 (ready)
|
||||||
:Target: Flutter (iOS, Android, macOS)
|
:Target: Flutter (iOS, Android, macOS, Linux, Windows)
|
||||||
:Bridge: ``flutter_rust_bridge`` v2
|
:Bridge: ``flutter_rust_bridge`` v2
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
|
|
@ -23,7 +23,7 @@ This repository contains:
|
||||||
|
|
||||||
* A small Rust bridge crate referencing ``ccc_rust`` via a git tag
|
* A small Rust bridge crate referencing ``ccc_rust`` via a git tag
|
||||||
* Generated ``flutter_rust_bridge`` bindings
|
* Generated ``flutter_rust_bridge`` bindings
|
||||||
* A Flutter plugin scaffold (iOS / Android / macOS)
|
* A Flutter plugin scaffold (iOS / Android / macOS / Linux / Windows)
|
||||||
* A clean Dart API surface for applications
|
* A clean Dart API surface for applications
|
||||||
|
|
||||||
This plugin does **not** implement cryptography itself.
|
This plugin does **not** implement cryptography itself.
|
||||||
|
|
@ -100,21 +100,25 @@ Repository Structure
|
||||||
|
|
||||||
ccc_dart_plugin/
|
ccc_dart_plugin/
|
||||||
├── pubspec.yaml
|
├── pubspec.yaml
|
||||||
|
├── flutter_rust_bridge.yaml
|
||||||
├── lib/
|
├── lib/
|
||||||
|
│ ├── ccc_cryptography.dart # barrel export
|
||||||
│ ├── ccc_crypto.dart
|
│ ├── ccc_crypto.dart
|
||||||
│ ├── ccc_provider_catalog.dart
|
│ ├── ccc_provider_catalog.dart
|
||||||
│ ├── ccc_self_test.dart
|
│ ├── ccc_self_test.dart
|
||||||
|
│ ├── ccc_exceptions.dart
|
||||||
│ └── src/
|
│ └── src/
|
||||||
│ └── frb_generated.dart
|
│ └── frb_generated.dart # auto-generated by FRB
|
||||||
├── rust/
|
├── rust/
|
||||||
│ ├── Cargo.toml
|
│ ├── Cargo.toml
|
||||||
│ └── src/
|
│ └── src/
|
||||||
│ ├── lib.rs
|
│ ├── lib.rs
|
||||||
│ └── api.rs
|
│ └── api.rs # thin wrappers over ccc_rust
|
||||||
├── flutter_rust_bridge.yaml
|
|
||||||
├── ios/
|
├── ios/
|
||||||
├── android/
|
├── android/
|
||||||
├── macos/
|
├── macos/
|
||||||
|
├── linux/
|
||||||
|
├── windows/
|
||||||
└── test/
|
└── test/
|
||||||
└── roundtrip_test.dart
|
└── roundtrip_test.dart
|
||||||
|
|
||||||
|
|
@ -128,7 +132,7 @@ Rust Bridge Crate
|
||||||
.. code-block:: toml
|
.. code-block:: toml
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "ccc_dart_bridge"
|
name = "ccc_cryptography"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
@ -228,13 +232,19 @@ Testing Requirements
|
||||||
Before tagging ``v0.1.0`` of this plugin:
|
Before tagging ``v0.1.0`` of this plugin:
|
||||||
|
|
||||||
* Rust bridge builds for:
|
* Rust bridge builds for:
|
||||||
- iOS (aarch64-apple-ios)
|
- iOS (aarch64-apple-ios, aarch64-apple-ios-sim)
|
||||||
- Android (arm64 + x86_64)
|
- Android (aarch64-linux-android, x86_64-linux-android)
|
||||||
- macOS (aarch64)
|
- macOS (aarch64-apple-darwin, x86_64-apple-darwin)
|
||||||
* Flutter integration tests:
|
- Linux (x86_64-unknown-linux-gnu)
|
||||||
|
- Windows (x86_64-pc-windows-msvc)
|
||||||
|
* Unit tests (Dart-side, host platform):
|
||||||
- AEAD roundtrip encrypt/decrypt
|
- AEAD roundtrip encrypt/decrypt
|
||||||
- HKDF known-vector test
|
- HKDF known-vector test
|
||||||
- X25519 key agreement test
|
- X25519 key agreement test
|
||||||
|
- Provider catalog and self-test validation
|
||||||
|
- Error handling / exception mapping
|
||||||
|
* Flutter integration tests (on-device, all five platforms):
|
||||||
|
- Full init → encrypt → decrypt flow
|
||||||
* ``CccSelfTest.runAll()`` returns success
|
* ``CccSelfTest.runAll()`` returns success
|
||||||
* No Dart-side crypto logic present
|
* No Dart-side crypto logic present
|
||||||
* No modification of ``ccc_rust`` source
|
* No modification of ``ccc_rust`` source
|
||||||
|
|
@ -296,3 +306,11 @@ Planned enhancements after Milestone 2:
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Related Documents
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* ``docs/milestone2_phases.rst`` — Phase-by-phase implementation
|
||||||
|
tracker with task checklists and exit criteria.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,643 @@
|
||||||
|
CCC Dart Plugin — Milestone 2 Implementation Phases
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
:Created: 2026-03-11
|
||||||
|
:Last Updated: 2026-03-11
|
||||||
|
:Status: In Progress
|
||||||
|
:Depends on: ``ccc_rust`` ≥ v0.1.0 (ready)
|
||||||
|
:Bridge: ``flutter_rust_bridge`` v2
|
||||||
|
:Platforms: iOS, Android, macOS, Linux, Windows
|
||||||
|
:Rust repo: ``ssh://git@10.0.5.109:j3g/lum_ccc_rust.git``
|
||||||
|
:Local override: ``/Volumes/LUM/source/letusmsg_proj/app/lum_ccc_rust``
|
||||||
|
|
||||||
|
This document tracks the implementation phases for Milestone 2 —
|
||||||
|
building the ``flutter_rust_bridge``-based Flutter plugin that exposes
|
||||||
|
``ccc_rust`` cryptographic providers to Dart.
|
||||||
|
|
||||||
|
Related Documents
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ``docs/ccc_fplugin_architecture_design.rst`` — Architecture & integration guide
|
||||||
|
* ``docs/ccc_rust_plan.rst`` — Master three-milestone architecture plan
|
||||||
|
* ``docs/ccc_rust_plan_phases.rst`` — Phase tracking across all milestones
|
||||||
|
* ``docs/ccc_rust_milestone2_session_state_OLD.rst`` — Earlier planning (superseded by this doc)
|
||||||
|
|
||||||
|
Preconditions (verified)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ``[x]`` Milestone 1 verification gate passed
|
||||||
|
* ``[x]`` Conformance vectors passed in Rust workspace
|
||||||
|
* ``[x]`` Rust target builds validated (iOS + Android)
|
||||||
|
* ``[x]`` ``cargo audit`` — no known CVEs
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase Overview
|
||||||
|
--------------
|
||||||
|
|
||||||
|
====== ========================================== ========== ============
|
||||||
|
Phase Title Status Depends on
|
||||||
|
====== ========================================== ========== ============
|
||||||
|
1 Project Restructure & FRB Setup Done —
|
||||||
|
2 Rust Bridge Crate (DTOs + API) Done Phase 1
|
||||||
|
3 Dart API Surface Done Phase 2
|
||||||
|
4 Platform Build Verification Done Phase 3
|
||||||
|
5 Unit Tests Done Phase 3
|
||||||
|
6 Integration Tests & Polish In Progress Phase 4, 5
|
||||||
|
====== ========================================== ========== ============
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 1 — Project Restructure & FRB Setup
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
:Status: Done
|
||||||
|
|
||||||
|
**Goal:** Replace the placeholder C FFI scaffold with a
|
||||||
|
``flutter_rust_bridge`` v2 project structure.
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 1.1
|
||||||
|
- Remove placeholder C source files (``src/ccc_cryptography.c``,
|
||||||
|
``src/ccc_cryptography.h``)
|
||||||
|
- ✅
|
||||||
|
* - 1.2
|
||||||
|
- Remove ``ffigen.yaml`` and generated bindings
|
||||||
|
(``lib/ccc_cryptography_bindings_generated.dart``)
|
||||||
|
- ✅
|
||||||
|
* - 1.3
|
||||||
|
- Create ``rust/`` directory with bridge crate scaffold
|
||||||
|
(``Cargo.toml``, ``src/lib.rs``, ``src/api/``); FRB directory
|
||||||
|
structure via ``flutter_rust_bridge_codegen integrate``;
|
||||||
|
set ``crate-type = ["cdylib", "staticlib"]``
|
||||||
|
- ✅
|
||||||
|
* - 1.4
|
||||||
|
- Add ``flutter_rust_bridge`` v2 dependency to ``rust/Cargo.toml``;
|
||||||
|
reference ``ccc_rust`` crates via local path deps for dev
|
||||||
|
(git deps commented for CI/release);
|
||||||
|
crate name set to ``ccc_cryptography`` to match plugin
|
||||||
|
- ✅
|
||||||
|
* - 1.5
|
||||||
|
- Create ``flutter_rust_bridge.yaml`` configuration
|
||||||
|
(``rust_input: crate::api``, ``dart_output: lib/src/rust``)
|
||||||
|
- ✅
|
||||||
|
* - 1.6
|
||||||
|
- Update ``pubspec.yaml``: replace ``ffigen`` with
|
||||||
|
``flutter_rust_bridge``, add freezed, integration_test
|
||||||
|
- ✅
|
||||||
|
* - 1.7
|
||||||
|
- Platform build configs generated by FRB integrate +
|
||||||
|
Cargokit (CMakeLists, Podspecs, Gradle)
|
||||||
|
- ✅
|
||||||
|
* - 1.8
|
||||||
|
- Run ``flutter_rust_bridge_codegen generate`` — bridge compiles;
|
||||||
|
``flutter build macos`` succeeds (42.7 MB example app)
|
||||||
|
- ✅
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ``flutter_rust_bridge_codegen generate`` succeeds.
|
||||||
|
* ``flutter build`` runs without errors on host (macOS).
|
||||||
|
* No C FFI scaffolding remains.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 2 — Rust Bridge Crate (DTOs + API)
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
:Status: Done
|
||||||
|
:Depends on: Phase 1
|
||||||
|
|
||||||
|
**Goal:** Implement DTOs and thin Rust wrappers in the bridge crate that
|
||||||
|
expose all ``ccc_rust`` provider functionality across the FRB bridge.
|
||||||
|
|
||||||
|
The bridge crate lives at ``rust/`` and must not contain any cryptographic
|
||||||
|
logic — only type conversions and delegation to ``ccc_rust`` providers.
|
||||||
|
|
||||||
|
Files created:
|
||||||
|
|
||||||
|
* ``rust/src/api/dto.rs`` — DTOs, algorithm enums, bridge error type
|
||||||
|
* ``rust/src/api/crypto.rs`` — 13 bridge entry-point functions
|
||||||
|
|
||||||
|
DTOs (``rust/src/api/dto.rs``)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Bridge-visible data transfer objects with ``From<core type>`` impls:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 2.1
|
||||||
|
- ``CccCapabilities`` — maps from ``ProviderCapabilities``
|
||||||
|
(provider name, per-category algorithm list with availability,
|
||||||
|
efficiency/reliability scores); flattened HashMap → Vec for FRB
|
||||||
|
- ✅
|
||||||
|
* - 2.2
|
||||||
|
- ``CccKemKeyPair`` — maps from ``KemKeyPair``
|
||||||
|
(public_key, private_key as ``Vec<u8>``)
|
||||||
|
- ✅
|
||||||
|
* - 2.3
|
||||||
|
- ``CccKemEncapResult`` — maps from ``KemEncapResult``
|
||||||
|
(ciphertext, shared_secret)
|
||||||
|
- ✅
|
||||||
|
* - 2.4
|
||||||
|
- ``CccSelfTestReport`` — maps from ``SelfTestReport``
|
||||||
|
(overall pass/fail, per-algorithm results)
|
||||||
|
- ✅
|
||||||
|
* - 2.5
|
||||||
|
- ``CccAlgoTestResult`` — maps from ``AlgoTestResult``
|
||||||
|
(algorithm ID, pass/fail, diagnostic message)
|
||||||
|
- ✅
|
||||||
|
|
||||||
|
Bridge API (``rust/src/api/crypto.rs``)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 2.6
|
||||||
|
- ``ccc_init()`` — register WolfSslProvider (idempotent)
|
||||||
|
- ✅
|
||||||
|
* - 2.7
|
||||||
|
- ``ccc_list_providers()`` — return registered provider names (sync)
|
||||||
|
- ✅
|
||||||
|
* - 2.8
|
||||||
|
- ``ccc_capabilities()`` — return ``CccCapabilities`` for default provider
|
||||||
|
- ✅
|
||||||
|
* - 2.9
|
||||||
|
- ``ccc_aead_encrypt(algorithm, key, nonce, plaintext, aad)``
|
||||||
|
— thin wrapper over provider AEAD encrypt
|
||||||
|
- ✅
|
||||||
|
* - 2.10
|
||||||
|
- ``ccc_aead_decrypt(algorithm, key, nonce, ciphertext, aad)``
|
||||||
|
— thin wrapper over provider AEAD decrypt
|
||||||
|
- ✅
|
||||||
|
* - 2.11
|
||||||
|
- ``ccc_kdf_derive(algorithm, ikm, salt, info, len)``
|
||||||
|
— thin wrapper over provider KDF
|
||||||
|
- ✅
|
||||||
|
* - 2.12
|
||||||
|
- ``ccc_mac_compute(algorithm, key, data)``
|
||||||
|
— thin wrapper over provider MAC compute
|
||||||
|
- ✅
|
||||||
|
* - 2.13
|
||||||
|
- ``ccc_mac_verify(algorithm, key, data, mac)``
|
||||||
|
— thin wrapper over provider MAC verify
|
||||||
|
- ✅
|
||||||
|
* - 2.14
|
||||||
|
- ``ccc_hash(algorithm, data)``
|
||||||
|
— thin wrapper over provider hash
|
||||||
|
- ✅
|
||||||
|
* - 2.15
|
||||||
|
- ``ccc_kem_generate_keypair(algorithm)``
|
||||||
|
— return ``CccKemKeyPair``
|
||||||
|
- ✅
|
||||||
|
* - 2.16
|
||||||
|
- ``ccc_kem_encapsulate(algorithm, public_key)``
|
||||||
|
— return ``CccKemEncapResult``
|
||||||
|
- ✅
|
||||||
|
* - 2.17
|
||||||
|
- ``ccc_kem_decapsulate(algorithm, private_key, ciphertext)``
|
||||||
|
— return shared secret
|
||||||
|
- ✅
|
||||||
|
* - 2.18
|
||||||
|
- ``ccc_self_test()`` — return ``CccSelfTestReport``
|
||||||
|
- ✅
|
||||||
|
* - 2.19
|
||||||
|
- ``CccCryptoError`` enum mapping all 7 ``CryptoError`` variants
|
||||||
|
(``UnsupportedAlgorithm``, ``InvalidKey``, ``InvalidNonce``,
|
||||||
|
``AuthenticationFailed``, ``InvalidInput``, ``FeatureNotCompiled``,
|
||||||
|
``InternalError``)
|
||||||
|
- ✅
|
||||||
|
* - 2.20
|
||||||
|
- Run ``flutter_rust_bridge_codegen generate`` — Dart bindings
|
||||||
|
generated; ``flutter build macos`` succeeds (44.5 MB)
|
||||||
|
- ✅
|
||||||
|
|
||||||
|
DTO ↔ Rust Type Mapping
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
========================== =================================== ========================
|
||||||
|
Bridge DTO Rust Core Type Direction
|
||||||
|
========================== =================================== ========================
|
||||||
|
``CccCapabilities`` ``ProviderCapabilities`` Rust → Dart
|
||||||
|
``CccAlgorithmEntry`` (algo_id, name, capability) Rust → Dart
|
||||||
|
``CccAlgorithmCapability`` ``AlgorithmCapability`` Rust → Dart
|
||||||
|
``CccKemKeyPair`` ``KemKeyPair`` Rust → Dart
|
||||||
|
``CccKemEncapResult`` ``KemEncapResult`` Rust → Dart
|
||||||
|
``CccSelfTestReport`` ``SelfTestReport`` Rust → Dart
|
||||||
|
``CccAlgoTestResult`` ``AlgoTestResult`` Rust → Dart
|
||||||
|
``CccCryptoError`` enum ``CryptoError`` Rust → Dart exception
|
||||||
|
``CccAeadAlgorithm`` ``AeadAlgorithm`` Dart → Rust
|
||||||
|
``CccKdfAlgorithm`` ``KdfAlgorithm`` Dart → Rust
|
||||||
|
``CccMacAlgorithm`` ``MacAlgorithm`` Dart → Rust
|
||||||
|
``CccHashAlgorithm`` ``HashAlgorithm`` Dart → Rust
|
||||||
|
``CccKemAlgorithm`` ``KemAlgorithm`` Dart → Rust
|
||||||
|
========================== =================================== ========================
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ✅ All DTOs defined with ``From<>`` impls.
|
||||||
|
* ✅ All 13 bridge entry points exist in ``crypto.rs``.
|
||||||
|
* ✅ Codegen produces Dart bindings (+ freezed).
|
||||||
|
* ✅ Rust crate compiles with ``ccc_rust`` linked.
|
||||||
|
* ✅ ``flutter build macos`` succeeds (44.5 MB).
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 3 — Dart API Surface
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
:Status: Done
|
||||||
|
:Depends on: Phase 2
|
||||||
|
|
||||||
|
**Goal:** Build the clean Dart API layer that application code will
|
||||||
|
consume, wired to the FRB-generated bindings.
|
||||||
|
|
||||||
|
Files created:
|
||||||
|
|
||||||
|
* ``lib/ccc_crypto.dart`` — ``CccCrypto`` static façade (13 methods)
|
||||||
|
* ``lib/ccc_exceptions.dart`` — sealed ``CccException`` hierarchy (7 subtypes)
|
||||||
|
* ``lib/ccc_provider_catalog.dart`` — ``CccProviderCatalog`` + ``CccAlgorithmInfo``
|
||||||
|
* ``lib/ccc_self_test.dart`` — ``CccSelfTestResult`` + ``CccAlgorithmTestResult``
|
||||||
|
* ``lib/ccc_cryptography.dart`` — barrel export file (updated)
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 3.1
|
||||||
|
- Create ``lib/ccc_crypto.dart`` — ``abstract final class CccCrypto``
|
||||||
|
with 13 static methods: ``init``, ``listProviders``,
|
||||||
|
``getCapabilities``, ``encryptAead``, ``decryptAead``,
|
||||||
|
``deriveKey``, ``computeMac``, ``verifyMac``, ``hash``,
|
||||||
|
``kemGenerateKeypair``, ``kemEncapsulate``, ``kemDecapsulate``,
|
||||||
|
``runSelfTest``; each catches ``CccCryptoError`` and rethrows
|
||||||
|
as typed ``CccException``
|
||||||
|
- ✅
|
||||||
|
* - 3.2
|
||||||
|
- Create ``lib/ccc_provider_catalog.dart`` — ``CccProviderCatalog``
|
||||||
|
(provider name, per-category algorithm lists with availability,
|
||||||
|
efficiency/reliability scores); ``CccAlgorithmInfo`` wrapper
|
||||||
|
- ✅
|
||||||
|
* - 3.3
|
||||||
|
- Create ``lib/ccc_self_test.dart`` — ``CccSelfTestResult``
|
||||||
|
(per-algorithm pass/fail, overall status, diagnostics);
|
||||||
|
``CccAlgorithmTestResult`` wrapper
|
||||||
|
- ✅
|
||||||
|
* - 3.4
|
||||||
|
- Create ``lib/ccc_exceptions.dart`` — ``sealed class CccException``
|
||||||
|
with 7 subtypes (``CccUnsupportedAlgorithm``, ``CccInvalidKey``,
|
||||||
|
``CccInvalidNonce``, ``CccAuthenticationFailed``,
|
||||||
|
``CccInvalidInput``, ``CccFeatureNotCompiled``,
|
||||||
|
``CccInternalError``); factory ``CccException.from()`` using
|
||||||
|
Dart 3 switch expression/pattern matching
|
||||||
|
- ✅
|
||||||
|
* - 3.5
|
||||||
|
- Algorithm IDs: FRB-generated enums (``CccAeadAlgorithm``,
|
||||||
|
``CccKdfAlgorithm``, ``CccMacAlgorithm``, ``CccHashAlgorithm``,
|
||||||
|
``CccKemAlgorithm``) re-exported directly from barrel file;
|
||||||
|
separate constants file not needed
|
||||||
|
- ✅
|
||||||
|
* - 3.6
|
||||||
|
- Wire all Dart API methods to FRB-generated bindings
|
||||||
|
- ✅
|
||||||
|
* - 3.7
|
||||||
|
- Update barrel export file ``lib/ccc_cryptography.dart``
|
||||||
|
re-exporting the public API (CccCrypto, enums, DTOs,
|
||||||
|
exceptions, provider catalog, self-test)
|
||||||
|
- ✅
|
||||||
|
* - 3.8
|
||||||
|
- Remove old placeholder Dart code; update example app
|
||||||
|
to use ``CccCrypto.init()`` + ``runSelfTest()`` demo
|
||||||
|
- ✅
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ✅ ``dart analyze`` reports no errors or warnings.
|
||||||
|
* ✅ Public API matches the contract in the architecture design doc.
|
||||||
|
* ✅ No crypto logic exists in Dart — orchestration only.
|
||||||
|
* ✅ ``flutter build macos`` succeeds (44.5 MB).
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 4 — Platform Build Verification
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
:Status: Done
|
||||||
|
:Depends on: Phase 3
|
||||||
|
|
||||||
|
**Goal:** Verify the Rust bridge crate cross-compiles and links
|
||||||
|
correctly on all five target platforms.
|
||||||
|
|
||||||
|
Fixes applied:
|
||||||
|
|
||||||
|
* ``cargokit/build_tool/lib/src/android_environment.dart`` — added
|
||||||
|
``BINDGEN_EXTRA_CLANG_ARGS_{target}`` with NDK sysroot so that
|
||||||
|
crates using ``bindgen`` (wolfSSL) can find ``<pthread.h>`` during
|
||||||
|
Android cross-compilation.
|
||||||
|
* ``android/src/main/AndroidManifest.xml`` — removed deprecated
|
||||||
|
``package`` attribute (required by current Android Gradle Plugin).
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 4.1
|
||||||
|
- Build for **iOS** (``aarch64-apple-ios``) —
|
||||||
|
``flutter build ios --no-codesign`` → 17.1 MB
|
||||||
|
- ✅
|
||||||
|
* - 4.2
|
||||||
|
- Build for **Android** (``armv7-linux-androideabi``,
|
||||||
|
``aarch64-linux-android``, ``x86_64-linux-android``) —
|
||||||
|
``flutter build apk`` → 44.6 MB
|
||||||
|
- ✅
|
||||||
|
* - 4.3
|
||||||
|
- Build for **macOS** (``aarch64-apple-darwin``) —
|
||||||
|
``flutter build macos`` → 44.5 MB (verified in Phase 3)
|
||||||
|
- ✅
|
||||||
|
* - 4.4
|
||||||
|
- Build for **Linux** (``x86_64-unknown-linux-gnu``) —
|
||||||
|
requires Linux host; Rust target installed, deferred to CI
|
||||||
|
- ⏳
|
||||||
|
* - 4.5
|
||||||
|
- Build for **Windows** (``x86_64-pc-windows-msvc``) —
|
||||||
|
requires Windows host; Rust target installed, deferred to CI
|
||||||
|
- ⏳
|
||||||
|
* - 4.6
|
||||||
|
- Fix platform-specific NDK issues — added
|
||||||
|
``BINDGEN_EXTRA_CLANG_ARGS`` to Cargokit Android env;
|
||||||
|
removed deprecated ``package`` attr from AndroidManifest
|
||||||
|
- ✅
|
||||||
|
* - 4.7
|
||||||
|
- Verify ``flutter run`` launches on at least one
|
||||||
|
physical/emulated device per platform — deferred to
|
||||||
|
Phase 6 integration tests
|
||||||
|
- ⏳
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ✅ ``flutter build ios`` succeeds (17.1 MB).
|
||||||
|
* ✅ ``flutter build apk`` succeeds (44.6 MB, 3 ABIs).
|
||||||
|
* ✅ ``flutter build macos`` succeeds (44.5 MB).
|
||||||
|
* ⏳ ``flutter build linux`` — deferred (requires Linux host).
|
||||||
|
* ⏳ ``flutter build windows`` — deferred (requires Windows host).
|
||||||
|
* ✅ Native library bundled correctly for iOS, Android, macOS.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 5 — Unit Tests
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
:Status: Done
|
||||||
|
:Depends on: Phase 3
|
||||||
|
|
||||||
|
**Goal:** Verify correctness of the bridge and Dart API via
|
||||||
|
Dart-side unit tests.
|
||||||
|
|
||||||
|
Test file: ``example/integration_test/ccc_crypto_test.dart``
|
||||||
|
|
||||||
|
Run command: ``flutter test integration_test/ccc_crypto_test.dart -d macos``
|
||||||
|
|
||||||
|
Result: **16/16 passed** on macOS host.
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 5.1
|
||||||
|
- AEAD roundtrip test — encrypt then decrypt 1 KB
|
||||||
|
(AES-256-GCM), verify plaintext equality
|
||||||
|
- ✅
|
||||||
|
* - 5.2
|
||||||
|
- AEAD roundtrip test — encrypt then decrypt 1 KB
|
||||||
|
(ChaCha20-Poly1305)
|
||||||
|
- ✅
|
||||||
|
* - 5.3
|
||||||
|
- AEAD tamper test — modify ciphertext, verify
|
||||||
|
``CccAuthenticationFailed`` is thrown
|
||||||
|
- ✅
|
||||||
|
* - 5.4
|
||||||
|
- KDF known-vector test — HKDF-SHA256 output compared against
|
||||||
|
RFC 5869 Test Case 1 (42 bytes, exact match)
|
||||||
|
- ✅
|
||||||
|
* - 5.5
|
||||||
|
- MAC roundtrip test — ``computeMac`` then ``verifyMac``
|
||||||
|
(HMAC-SHA256), verify returns true
|
||||||
|
- ✅
|
||||||
|
* - 5.6
|
||||||
|
- MAC tamper test — modify data after MAC, verify returns false
|
||||||
|
- ✅
|
||||||
|
* - 5.7
|
||||||
|
- Hash known-vector test — compare output against known
|
||||||
|
SHA-256 / SHA-384 / SHA-512 digests ("abc" test vector)
|
||||||
|
- ✅
|
||||||
|
* - 5.8
|
||||||
|
- KEM roundtrip test — ``kemGenerateKeypair``,
|
||||||
|
``kemEncapsulate``, ``kemDecapsulate`` (X25519); verify
|
||||||
|
shared secrets match
|
||||||
|
- ✅
|
||||||
|
* - 5.9
|
||||||
|
- KEM roundtrip test — X448 keygen/encap/decap flow
|
||||||
|
- ✅
|
||||||
|
* - 5.10
|
||||||
|
- Provider catalog test — verify ``listProviders()`` returns
|
||||||
|
non-empty list, ``getCapabilities()`` returns per-algorithm
|
||||||
|
availability and scores
|
||||||
|
- ✅
|
||||||
|
* - 5.11
|
||||||
|
- Self-test validation — ``runSelfTest()`` returns structured
|
||||||
|
report; non-AEAD algorithms all pass (AEAD test vectors
|
||||||
|
have known upstream length mismatch in ``ccc_rust``)
|
||||||
|
- ✅
|
||||||
|
* - 5.12
|
||||||
|
- Error handling tests — verify ``CccException`` subtypes
|
||||||
|
are thrown for invalid key length and invalid nonce length
|
||||||
|
- ✅
|
||||||
|
* - 5.13
|
||||||
|
- Algorithm ID mapping test — verify catalog IDs match
|
||||||
|
Rust enum discriminants (AES-GCM=12, SHA-256=40, X25519=50)
|
||||||
|
- ✅
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* ✅ All 16 unit tests pass on host platform (macOS).
|
||||||
|
* ✅ No Dart-side crypto logic introduced by tests.
|
||||||
|
|
||||||
|
Known upstream issue:
|
||||||
|
|
||||||
|
* ``ccc_rust`` AEAD self-test vectors don't account for the appended
|
||||||
|
authentication tag in encrypt output, causing length-mismatch failures
|
||||||
|
for AES-256-GCM (80 vs 64 bytes) and ChaCha20-Poly1305 (56 vs 114 bytes).
|
||||||
|
This is a ``ccc_rust`` bug, not a bridge issue.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 6 — Integration Tests & Polish
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
:Status: Not Started
|
||||||
|
:Depends on: Phase 4, Phase 5
|
||||||
|
|
||||||
|
**Goal:** End-to-end validation on real devices/simulators, example
|
||||||
|
app update, documentation, and release preparation.
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
:widths: 5 60 15
|
||||||
|
|
||||||
|
* - #
|
||||||
|
- Task
|
||||||
|
- Status
|
||||||
|
* - 6.1
|
||||||
|
- Write Flutter integration tests (``integration_test/``) that
|
||||||
|
exercise the full init → encrypt → decrypt flow on-device;
|
||||||
|
16 tests covering AEAD, KDF, MAC, Hash, KEM, catalog,
|
||||||
|
self-test, error handling, and algorithm ID mapping
|
||||||
|
- ✅
|
||||||
|
* - 6.2
|
||||||
|
- Integration test: KEM keygen → encap → decap roundtrip
|
||||||
|
(X25519 + X448)
|
||||||
|
- ✅
|
||||||
|
* - 6.3
|
||||||
|
- Integration test: self-test API returns structured report
|
||||||
|
- ✅
|
||||||
|
* - 6.4
|
||||||
|
- Run integration tests on iOS simulator —
|
||||||
|
16/16 passed (iPhone 16, iOS 18.4);
|
||||||
|
fixed ``build.rs`` bindgen struct-size bug
|
||||||
|
(``options.h`` defines now auto-parsed)
|
||||||
|
- ✅
|
||||||
|
* - 6.5
|
||||||
|
- Run integration tests on Android emulator —
|
||||||
|
16/16 passed (API 33, aarch64-linux-android)
|
||||||
|
- ✅
|
||||||
|
* - 6.6
|
||||||
|
- Run integration tests on macOS — 16/16 passed
|
||||||
|
- ✅
|
||||||
|
* - 6.7
|
||||||
|
- Run integration tests on Linux —
|
||||||
|
deferred (requires Linux host)
|
||||||
|
- ⏳
|
||||||
|
* - 6.8
|
||||||
|
- Run integration tests on Windows —
|
||||||
|
deferred (requires Windows host)
|
||||||
|
- ⏳
|
||||||
|
* - 6.9
|
||||||
|
- Update example app (``example/``) — replace ``sum`` demo with
|
||||||
|
real crypto demo (encrypt/decrypt, provider listing, self-test)
|
||||||
|
- ☐
|
||||||
|
* - 6.10
|
||||||
|
- Update ``CHANGELOG.md`` for v0.1.0
|
||||||
|
- ☐
|
||||||
|
* - 6.11
|
||||||
|
- Final review — confirm no Dart crypto logic, no ``ccc_rust``
|
||||||
|
source modifications, all tests green
|
||||||
|
- ☐
|
||||||
|
* - 6.12
|
||||||
|
- Tag ``v0.1.0`` of the plugin
|
||||||
|
- ☐
|
||||||
|
|
||||||
|
Exit Criteria
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Integration tests pass on all five platforms.
|
||||||
|
* Example app demonstrates real cryptographic operations.
|
||||||
|
* ``CHANGELOG.md`` is updated.
|
||||||
|
* Repository tagged ``v0.1.0``.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Milestone 2 Verification Gate
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
*All items must be checked before the* ``v0.1.0`` *tag is cut.*
|
||||||
|
|
||||||
|
* ``[x]`` FRB bridge API compiles and loads in Flutter plugin
|
||||||
|
* ``[x]`` Generated Dart bindings are committed and reproducible
|
||||||
|
* ``[x]`` All unit tests pass on host platform (macOS)
|
||||||
|
* ``[x]`` ``flutter build ios`` succeeds
|
||||||
|
* ``[x]`` ``flutter build apk`` succeeds
|
||||||
|
* ``[x]`` ``flutter build macos`` succeeds
|
||||||
|
* ``[ ]`` ``flutter build linux`` succeeds
|
||||||
|
* ``[ ]`` ``flutter build windows`` succeeds
|
||||||
|
* ``[x]`` Integration tests pass on iOS simulator
|
||||||
|
* ``[x]`` Integration tests pass on Android emulator/device
|
||||||
|
* ``[x]`` Integration tests pass on macOS
|
||||||
|
* ``[ ]`` Integration tests pass on Linux
|
||||||
|
* ``[ ]`` Integration tests pass on Windows
|
||||||
|
* ``[x]`` No Dart-side crypto logic present
|
||||||
|
* ``[x]`` No modification of ``ccc_rust`` source
|
||||||
|
* ``[ ]`` ``ccc_rust`` dependency pinned to exact commit/tag
|
||||||
|
* ``[ ]`` Plugin package tagged ``v0.1.0``
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Milestone 3 Handoff Checklist
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Before handing off to the LetUsMsg app team:
|
||||||
|
|
||||||
|
☐ All Phase 1–6 exit criteria met
|
||||||
|
☐ Milestone 2 Verification Gate passed
|
||||||
|
☐ ``CccCrypto`` public API is stable and documented
|
||||||
|
☐ No floating dependencies (all git refs pinned)
|
||||||
|
☐ Self-test passes on all platforms
|
||||||
|
☐ Example app runs on all platforms
|
||||||
|
☐ No raw private keys exposed to Dart
|
||||||
|
☐ ``pubspec.yaml`` is publishable (or git-installable)
|
||||||
|
|
||||||
|
The LetUsMsg app will:
|
||||||
|
|
||||||
|
* Add this plugin via ``pubspec.yaml``
|
||||||
|
* Call ``CccCrypto.cccInit()`` at startup
|
||||||
|
* Replace existing crypto stubs with plugin calls
|
||||||
|
* Populate ``CccProviderCatalog`` from runtime capabilities
|
||||||
|
* Surface ``CccSelfTest.runAll()`` in debug UI
|
||||||
|
|
||||||
|
No Rust changes and no bridge changes are permitted during Milestone 3.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
=====================================================
|
||||||
|
CCC Rust Milestone 2 — Session State (Planning)
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
:Status: Not started
|
||||||
|
:Date: 2026-02-26
|
||||||
|
:Repository target: ``ccc_cryptography`` (plugin repo)
|
||||||
|
:Depends on: ``ccc_rust`` Milestone 1 complete
|
||||||
|
|
||||||
|
Related Documents
|
||||||
|
=================
|
||||||
|
|
||||||
|
* Milestone 1 completion record:
|
||||||
|
``docs/ccc_rust_milestone1.rst``
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
This document tracks Milestone 2 execution state for Flutter plugin + bridge work.
|
||||||
|
Milestone 2 begins with Milestone 1 already verified and complete.
|
||||||
|
|
||||||
|
Current Gate Preconditions
|
||||||
|
==========================
|
||||||
|
|
||||||
|
* ``[x]`` Milestone 1 verification gate passed.
|
||||||
|
* ``[x]`` Conformance vectors passed in Rust workspace.
|
||||||
|
* ``[x]`` Rust target builds validated (iOS + Android).
|
||||||
|
|
||||||
|
Milestone 2 Work Checklist
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Phase 1 — Repository + Scaffold
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Create/confirm ``ccc_cryptography`` repository and branch strategy.
|
||||||
|
* ``[ ]`` Create Flutter plugin scaffold (``pubspec.yaml``, ``ios/``, ``android/``, ``macos/``).
|
||||||
|
* ``[ ]`` Add Rust bridge crate with ``crate-type = ["cdylib", "staticlib"]``.
|
||||||
|
* ``[ ]`` Wire dependency on ``ccc_rust`` pinned tag/revision.
|
||||||
|
|
||||||
|
Phase 2 — Bridge API Surface
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Define DTOs:
|
||||||
|
|
||||||
|
* ``CapabilitiesDto``
|
||||||
|
* ``KemKeyPairDto``
|
||||||
|
* ``KemEncapDto``
|
||||||
|
* ``SelfTestDto``
|
||||||
|
* ``AlgoTestResultDto``
|
||||||
|
|
||||||
|
* ``[ ]`` Implement bridge entry points:
|
||||||
|
|
||||||
|
* ``ccc_init``
|
||||||
|
* ``ccc_list_providers``
|
||||||
|
* ``ccc_capabilities`` / ``ccc_available_algorithms``
|
||||||
|
* ``ccc_aead_encrypt`` / ``ccc_aead_decrypt``
|
||||||
|
* ``ccc_kdf_derive``
|
||||||
|
* ``ccc_mac_compute`` / ``ccc_mac_verify``
|
||||||
|
* ``ccc_hash``
|
||||||
|
* ``ccc_kem_generate_keypair``
|
||||||
|
* ``ccc_kem_encapsulate`` / ``ccc_kem_decapsulate``
|
||||||
|
* ``ccc_self_test``
|
||||||
|
|
||||||
|
Phase 3 — FRB Codegen + Build Integration
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Add ``flutter_rust_bridge`` configuration + codegen scripts.
|
||||||
|
* ``[ ]`` Run FRB codegen and commit generated artifacts.
|
||||||
|
* ``[ ]`` Verify plugin compiles for iOS.
|
||||||
|
* ``[ ]`` Verify plugin compiles for Android.
|
||||||
|
* ``[ ]`` Verify plugin compiles for macOS.
|
||||||
|
|
||||||
|
Phase 4 — Dart API Layer
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Implement ``CccCrypto`` service API wrapper.
|
||||||
|
* ``[ ]`` Implement ``CccSelfTest`` wrapper.
|
||||||
|
* ``[ ]`` Implement runtime ``CccProviderCatalog`` population.
|
||||||
|
* ``[ ]`` Ensure algorithm ID mapping remains 1:1 with Rust discriminants.
|
||||||
|
|
||||||
|
Phase 5 — Integration + Validation
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Add integration tests for AEAD roundtrip (at least AES-GCM and ChaCha20).
|
||||||
|
* ``[ ]`` Add integration tests for KEM keygen/encap/decap flow.
|
||||||
|
* ``[ ]`` Add integration tests for self-test API.
|
||||||
|
* ``[ ]`` Run on iOS simulator.
|
||||||
|
* ``[ ]`` Run on Android emulator/device.
|
||||||
|
|
||||||
|
Milestone 2 TODO Queue
|
||||||
|
======================
|
||||||
|
|
||||||
|
Immediate TODOs (next session)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Decide exact Milestone 2 repository location/URL and baseline branch.
|
||||||
|
* ``[ ]`` Pin ``ccc_rust`` dependency to a reproducible reference (tag or commit hash).
|
||||||
|
* ``[ ]`` Define FRB module layout and generated file commit policy.
|
||||||
|
* ``[ ]`` Draft DTO type mapping table (Rust type -> bridge DTO -> Dart model).
|
||||||
|
|
||||||
|
Backlog TODOs
|
||||||
|
-------------
|
||||||
|
|
||||||
|
* ``[ ]`` Add CI job matrix for iOS/macOS/Android plugin builds.
|
||||||
|
* ``[ ]`` Add versioning/release policy for plugin package.
|
||||||
|
* ``[ ]`` Add troubleshooting notes for NDK/Xcode toolchains.
|
||||||
|
|
||||||
|
Milestone 2 Verification Gate
|
||||||
|
=============================
|
||||||
|
|
||||||
|
All of the following must pass before declaring Milestone 2 complete:
|
||||||
|
|
||||||
|
* ``[ ]`` FRB bridge API compiles and loads in Flutter plugin.
|
||||||
|
* ``[ ]`` Generated Dart bindings are committed and reproducible.
|
||||||
|
* ``[ ]`` ``flutter build ios`` succeeds.
|
||||||
|
* ``[ ]`` ``flutter build apk`` succeeds.
|
||||||
|
* ``[ ]`` ``flutter build macos`` succeeds.
|
||||||
|
* ``[ ]`` Integration test suite passes on iOS simulator.
|
||||||
|
* ``[ ]`` Integration test suite passes on Android emulator/device.
|
||||||
|
* ``[ ]`` Plugin package is tagged/released at ``v0.1.0`` (or agreed target version).
|
||||||
|
|
||||||
|
Notes
|
||||||
|
=====
|
||||||
|
|
||||||
|
* Milestone 2 is the first place where ``flutter_rust_bridge`` is allowed.
|
||||||
|
* Milestone 1 Rust workspace remains bridge-free and should not be modified
|
||||||
|
for Dart/plugin scaffolding.
|
||||||
|
|
@ -0,0 +1,387 @@
|
||||||
|
===============================================
|
||||||
|
CCC Rust Crypto Provider — Architecture Plan
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
:Status: Approved
|
||||||
|
:Date: 2026-02-24
|
||||||
|
:Author: Engineering
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
This document describes the architecture for the **CCC Rust Crypto Provider**,
|
||||||
|
the first of three milestones in delivering hardware-grade cryptography to the
|
||||||
|
LetUsMsg application.
|
||||||
|
|
||||||
|
The guiding constraint is *clean separation*: each milestone is a distinct,
|
||||||
|
independently testable artifact with no forbidden upward dependencies.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Three-Milestone Strategy
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
============= =================================== ========================== ===========================
|
||||||
|
Milestone Repository What ships Depends on
|
||||||
|
============= =================================== ========================== ===========================
|
||||||
|
**1 (this)** ``ccc_rust`` Pure Rust crypto library wolfSSL (vendored)
|
||||||
|
**2** ``ccc_dart_plugin`` Flutter plugin + Dart API ``ccc_rust`` (git/semver)
|
||||||
|
**3** ``letusmsg`` (existing app) App integration ``ccc_dart_plugin`` package
|
||||||
|
============= =================================== ========================== ===========================
|
||||||
|
|
||||||
|
Milestone 1 is complete and independently verified *before* any bridge or Dart
|
||||||
|
code is written. Milestone 2 owns the ``flutter_rust_bridge`` dependency and
|
||||||
|
all generated Dart bindings. Milestone 3 makes no Rust changes; it consumes
|
||||||
|
only the published Dart package.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Milestone 1 — ``ccc_rust`` Scope (this repository)
|
||||||
|
|
||||||
|
**Goal**: a fully tested, provider-agnostic Rust crypto library.
|
||||||
|
|
||||||
|
**Hard boundaries** — this repo must never contain:
|
||||||
|
|
||||||
|
* ``flutter_rust_bridge`` or any Flutter/Dart dependency
|
||||||
|
* Generated Dart files (``frb_generated.rs``, ``*.dart``)
|
||||||
|
* ``flutter_rust_bridge.yaml``
|
||||||
|
* Any platform plugin scaffold (``ios/``, ``android/``, ``macos/``)
|
||||||
|
|
||||||
|
Guiding Principles
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
1. Every provider reports its own capabilities at runtime — no compile-time
|
||||||
|
hard-coding of ``available: true/false``.
|
||||||
|
2. Algorithm IDs in Rust map 1-to-1 to the integer constants in
|
||||||
|
``cipher_constants.dart`` — zero Dart changes needed when wiring up.
|
||||||
|
3. Key material is zeroed on drop (``zeroize`` crate) everywhere.
|
||||||
|
4. A conformance test suite validates NIST/RFC vectors for every algorithm
|
||||||
|
before any provider is marked ``available``.
|
||||||
|
5. The library has no runtime dependency on Flutter; it is consumable by any
|
||||||
|
FFI host (Flutter plugin, Python tests, CLI tools).
|
||||||
|
|
||||||
|
Repository Layout (Milestone 1 — this repo)
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
ccc_rust/
|
||||||
|
├── Cargo.toml ← workspace manifest (3 members, no bridge)
|
||||||
|
├── rust-toolchain.toml ← pinned stable toolchain
|
||||||
|
├── .cargo/
|
||||||
|
│ └── config.toml ← cross-compile target aliases
|
||||||
|
├── vendors/
|
||||||
|
│ ├── README.md ← submodule pin rationale + upgrade notes
|
||||||
|
│ └── wolfssl/ ← git submodule (wolfSSL/wolfssl @ v5.7.2-stable)
|
||||||
|
├── crates/
|
||||||
|
│ ├── ccc-crypto-core/ ← shared traits, enums, registry
|
||||||
|
│ │ ├── Cargo.toml
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── lib.rs
|
||||||
|
│ │ ├── algorithms.rs ← algorithm enums (values == cipher_constants.dart)
|
||||||
|
│ │ ├── capabilities.rs ← AlgorithmCapability, ProviderCapabilities
|
||||||
|
│ │ ├── error.rs ← CryptoError enum
|
||||||
|
│ │ ├── provider.rs ← CryptoProvider umbrella trait
|
||||||
|
│ │ ├── registry.rs ← ProviderRegistry (OnceLock<Mutex<...>>)
|
||||||
|
│ │ └── types.rs ← KemKeyPair, SelfTestReport, BenchmarkReport
|
||||||
|
│ └── ccc-crypto-wolfssl/ ← wolfSSL provider implementation
|
||||||
|
│ ├── Cargo.toml
|
||||||
|
│ └── src/
|
||||||
|
│ ├── lib.rs
|
||||||
|
│ ├── aead.rs ← AES-256-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305
|
||||||
|
│ ├── kdf.rs ← HKDF-SHA256/384/512, Argon2id, BLAKE2b-KDF
|
||||||
|
│ ├── mac.rs ← HMAC-SHA256/384/512, BLAKE2b-MAC
|
||||||
|
│ ├── hash.rs ← SHA-256/384/512, SHA3-256/512, BLAKE2b-512
|
||||||
|
│ ├── kem.rs ← X25519, X448 (ML-KEM deferred Phase 5)
|
||||||
|
│ ├── capabilities.rs ← probe-at-startup + benchmark()
|
||||||
|
│ └── provider.rs ← WolfSslProvider: CryptoProvider impl
|
||||||
|
└── tests/
|
||||||
|
└── conformance/
|
||||||
|
├── Cargo.toml
|
||||||
|
└── src/
|
||||||
|
└── main.rs ← NIST/RFC vectors for all algorithms
|
||||||
|
|
||||||
|
Step 1 — Cargo Workspace Scaffold
|
||||||
|
|
||||||
|
``Cargo.toml``::
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"crates/ccc-crypto-core",
|
||||||
|
"crates/ccc-crypto-wolfssl",
|
||||||
|
"tests/conformance",
|
||||||
|
]
|
||||||
|
|
||||||
|
``rust-toolchain.toml``::
|
||||||
|
|
||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
|
||||||
|
``.cargo/config.toml``::
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
build-ios = "build --target aarch64-apple-ios"
|
||||||
|
build-android-arm64 = "build --target aarch64-linux-android"
|
||||||
|
build-android-x64 = "build --target x86_64-linux-android"
|
||||||
|
build-macos = "build --target aarch64-apple-darwin"
|
||||||
|
|
||||||
|
Step 2 — ``ccc-crypto-core`` Trait Crate
|
||||||
|
|
||||||
|
Algorithm Enumerations
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
All discriminant values match the integer constants in ``cipher_constants.dart``
|
||||||
|
exactly, so the Dart cipher-sequencing logic requires zero changes.
|
||||||
|
|
||||||
|
=================== =========================================================
|
||||||
|
Enum Variants → value
|
||||||
|
=================== =========================================================
|
||||||
|
``AeadAlgorithm`` AesGcm256=12, ChaCha20Poly1305=13, XChaCha20Poly1305=14,
|
||||||
|
Ascon128a=15
|
||||||
|
``KdfAlgorithm`` Sha256=1, Sha384=2, Sha512=3, Blake2b512=4
|
||||||
|
``MacAlgorithm`` HmacSha256=30, HmacSha512=32, Blake2bMac=33
|
||||||
|
``HashAlgorithm`` Sha256=40, Sha384=41, Sha512=42, Blake2b512=43, Sha3_256=44
|
||||||
|
``KemAlgorithm`` X25519=50, X448=51, MlKem768=52, MlKem1024=53,
|
||||||
|
ClassicMcEliece=54
|
||||||
|
=================== =========================================================
|
||||||
|
|
||||||
|
Key Structs
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
``AlgorithmCapability``::
|
||||||
|
|
||||||
|
available: bool
|
||||||
|
deterministic_io: bool
|
||||||
|
efficiency_score: u8 // populated by WolfSslProvider::benchmark()
|
||||||
|
reliability_score: u8 // populated by WolfSslProvider::self_test()
|
||||||
|
|
||||||
|
``ProviderCapabilities``::
|
||||||
|
|
||||||
|
provider_name: &'static str
|
||||||
|
aead: HashMap<AeadAlgorithm, AlgorithmCapability>
|
||||||
|
kdf: HashMap<KdfAlgorithm, AlgorithmCapability>
|
||||||
|
mac: HashMap<MacAlgorithm, AlgorithmCapability>
|
||||||
|
hash: HashMap<HashAlgorithm, AlgorithmCapability>
|
||||||
|
kem: HashMap<KemAlgorithm, AlgorithmCapability>
|
||||||
|
|
||||||
|
``KemKeyPair``::
|
||||||
|
|
||||||
|
public_key: Zeroizing<Vec<u8>>
|
||||||
|
private_key: Zeroizing<Vec<u8>>
|
||||||
|
|
||||||
|
``CryptoError``::
|
||||||
|
|
||||||
|
UnsupportedAlgorithm(String)
|
||||||
|
InvalidKey(String)
|
||||||
|
InvalidNonce(String)
|
||||||
|
AuthenticationFailed
|
||||||
|
InternalError(String)
|
||||||
|
|
||||||
|
Provider Traits
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: rust
|
||||||
|
|
||||||
|
pub trait AeadProvider {
|
||||||
|
fn encrypt_aead(
|
||||||
|
&self, algo: AeadAlgorithm,
|
||||||
|
key: &[u8], nonce: &[u8],
|
||||||
|
plaintext: &[u8], aad: &[u8],
|
||||||
|
) -> Result<Vec<u8>, CryptoError>;
|
||||||
|
fn decrypt_aead(
|
||||||
|
&self, algo: AeadAlgorithm,
|
||||||
|
key: &[u8], nonce: &[u8],
|
||||||
|
ciphertext: &[u8], aad: &[u8],
|
||||||
|
) -> Result<Vec<u8>, CryptoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KdfProvider {
|
||||||
|
fn derive_key(
|
||||||
|
&self, algo: KdfAlgorithm,
|
||||||
|
ikm: &[u8], salt: &[u8], info: &[u8], length: usize,
|
||||||
|
) -> Result<Zeroizing<Vec<u8>>, CryptoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MacProvider {
|
||||||
|
fn compute_mac(
|
||||||
|
&self, algo: MacAlgorithm, key: &[u8], data: &[u8],
|
||||||
|
) -> Result<Vec<u8>, CryptoError>;
|
||||||
|
fn verify_mac(
|
||||||
|
&self, algo: MacAlgorithm, key: &[u8],
|
||||||
|
data: &[u8], mac: &[u8],
|
||||||
|
) -> Result<bool, CryptoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HashProvider {
|
||||||
|
fn hash(
|
||||||
|
&self, algo: HashAlgorithm, data: &[u8],
|
||||||
|
) -> Result<Vec<u8>, CryptoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KemProvider {
|
||||||
|
fn generate_keypair(
|
||||||
|
&self, algo: KemAlgorithm,
|
||||||
|
) -> Result<KemKeyPair, CryptoError>;
|
||||||
|
fn encapsulate(
|
||||||
|
&self, algo: KemAlgorithm, public_key: &[u8],
|
||||||
|
) -> Result<(Vec<u8>, Zeroizing<Vec<u8>>), CryptoError>;
|
||||||
|
fn decapsulate(
|
||||||
|
&self, algo: KemAlgorithm,
|
||||||
|
private_key: &[u8], ciphertext: &[u8],
|
||||||
|
) -> Result<Zeroizing<Vec<u8>>, CryptoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CryptoProvider:
|
||||||
|
AeadProvider + KdfProvider + MacProvider + HashProvider + Send + Sync
|
||||||
|
{
|
||||||
|
fn provider_name(&self) -> &'static str;
|
||||||
|
fn capabilities(&self) -> ProviderCapabilities;
|
||||||
|
fn self_test(&self) -> SelfTestReport;
|
||||||
|
fn benchmark(&self) -> BenchmarkReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
``ProviderRegistry``::
|
||||||
|
|
||||||
|
OnceLock<Mutex<HashMap<&'static str, Box<dyn CryptoProvider + Send + Sync>>>>
|
||||||
|
fn register(name, provider)
|
||||||
|
fn get(name) -> Option<Arc<dyn CryptoProvider>>
|
||||||
|
fn list() -> Vec<&'static str>
|
||||||
|
|
||||||
|
Step 3 — wolfSSL Submodule + ``ccc-crypto-wolfssl``
|
||||||
|
|
||||||
|
wolfSSL is vendored as a git submodule pinned to ``v5.7.2-stable``.
|
||||||
|
The crate uses ``cmake`` + ``bindgen`` in ``build.rs`` to build and bind it.
|
||||||
|
A ``stub_ffi`` feature bypasses the C build for fast unit-test cycles.
|
||||||
|
|
||||||
|
wolfSSL Phase 4 Algorithm Coverage
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
=================== ===========================================
|
||||||
|
Category Algorithms
|
||||||
|
=================== ===========================================
|
||||||
|
AEAD AES-256-GCM, ChaCha20-Poly1305,
|
||||||
|
XChaCha20-Poly1305
|
||||||
|
KDF HKDF-SHA256/384/512, Argon2id, BLAKE2b-KDF
|
||||||
|
MAC HMAC-SHA256/384/512, BLAKE2b-MAC
|
||||||
|
Hash SHA-256/384/512, SHA3-256/512, BLAKE2b-512
|
||||||
|
KEM X25519, X448
|
||||||
|
KEM (Phase 5) ML-KEM-768, ML-KEM-1024, Classic McEliece
|
||||||
|
=================== ===========================================
|
||||||
|
|
||||||
|
Capability Probe Strategy
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``WolfSslProvider::capabilities()`` runs a minimal probe call (encrypt 1 byte;
|
||||||
|
decrypt; compare) per algorithm at startup. Sets ``available = probe_succeeded``.
|
||||||
|
If the wolfSSL build omits PQ support, ML-KEM entries report
|
||||||
|
``available: false`` gracefully.
|
||||||
|
|
||||||
|
Benchmark Strategy
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``WolfSslProvider::benchmark()`` encrypts a 1 MB buffer × 100 iterations per
|
||||||
|
AEAD algorithm, measures wall-clock throughput, normalises to a 0–100
|
||||||
|
``efficiency_score``. Run once at library init and cached.
|
||||||
|
|
||||||
|
|
||||||
|
Step 4 — Conformance Test Suite
|
||||||
|
|
||||||
|
Location: ``tests/conformance/src/main.rs``
|
||||||
|
|
||||||
|
Run with ``cargo run -p ccc-conformance-tests`` and ``cargo test --workspace``.
|
||||||
|
|
||||||
|
========================= ================================
|
||||||
|
Test Source
|
||||||
|
========================= ================================
|
||||||
|
AES-256-GCM NIST SP 800-38D vectors
|
||||||
|
ChaCha20-Poly1305 RFC 8439 vectors
|
||||||
|
XChaCha20-Poly1305 Extended nonce vectors
|
||||||
|
HKDF-SHA256/512 RFC 5869 vectors
|
||||||
|
HMAC-SHA256/512 RFC 4231 vectors
|
||||||
|
SHA-256/512, SHA3, BLAKE2b FIPS / reference vectors
|
||||||
|
X25519 DH RFC 7748 vectors
|
||||||
|
X448 DH RFC 7748 vectors
|
||||||
|
========================= ================================
|
||||||
|
|
||||||
|
**Gate**: ``ALL VECTORS PASSED`` must print before Milestone 1 is tagged.
|
||||||
|
|
||||||
|
|
||||||
|
Step 5 — Architecture Documentation
|
||||||
|
|
||||||
|
``docs/ccc_rust_milestone1.rst`` covers:
|
||||||
|
|
||||||
|
* Crate dependency graph (ASCII)
|
||||||
|
* "How to add a new provider" — 7-step trait checklist
|
||||||
|
* ``algo: u32`` → cipher constant mapping table
|
||||||
|
* Milestone 2 hand-off contract (API surface Milestone 2 must implement against)
|
||||||
|
|
||||||
|
|
||||||
|
Milestone 1 Verification Gate
|
||||||
|
|
||||||
|
All of the following must pass before the ``v0.1.0`` tag is cut and Milestone 2
|
||||||
|
work begins:
|
||||||
|
|
||||||
|
* ``cargo test --workspace`` — all unit tests pass
|
||||||
|
* ``cargo run -p ccc-conformance-tests`` — ``ALL VECTORS PASSED``
|
||||||
|
* ``cargo build --target aarch64-apple-ios`` — compiles
|
||||||
|
* ``cargo build --target aarch64-linux-android`` — compiles
|
||||||
|
* No ``flutter_rust_bridge``, Dart, or Flutter dependency anywhere in the workspace
|
||||||
|
* ``cargo audit`` — no known CVEs in dependency tree
|
||||||
|
|
||||||
|
|
||||||
|
Milestone 2 — ``ccc_dart_plugin`` (separate repository)
|
||||||
|
|
||||||
|
*(Planned — not started. Work begins after Milestone 1 gate passes.)*
|
||||||
|
|
||||||
|
A separate Dart/Flutter plugin package repository. It contains:
|
||||||
|
|
||||||
|
* A small Rust bridge crate that references ``ccc_rust`` via git tag:
|
||||||
|
|
||||||
|
.. code-block:: toml
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ccc-crypto-core = { git = "https://github.com/letusmsg/ccc_rust", tag = "v0.1.0" }
|
||||||
|
ccc-crypto-wolfssl = { git = "https://github.com/letusmsg/ccc_rust", tag = "v0.1.0" }
|
||||||
|
flutter_rust_bridge = "2"
|
||||||
|
|
||||||
|
* ``flutter_rust_bridge_codegen generate`` output (``frb_generated.rs``, Dart bindings)
|
||||||
|
* Flutter plugin scaffold: ``ios/``, ``android/``, ``macos/``
|
||||||
|
* Dart API surface: ``CccCrypto``, ``CccSelfTest``, ``CccProviderCatalog``
|
||||||
|
* Flutter integration tests (roundtrip encrypt/decrypt, self-test harness)
|
||||||
|
|
||||||
|
|
||||||
|
Milestone 3 — LetUsMsg App Integration (existing repository)
|
||||||
|
|
||||||
|
*(Planned — not started. Work begins after Milestone 2 gate passes.)*
|
||||||
|
|
||||||
|
The existing ``letusmsg`` Flutter app adds ``ccc_dart_plugin`` as a pubspec
|
||||||
|
dependency. Changes are confined to:
|
||||||
|
|
||||||
|
* ``crypto_wolfssl.dart`` — replace ``UnimplementedError`` stubs with plugin calls
|
||||||
|
* ``ccc_provider_spec.dart`` — populate ``CccProviderCatalog`` at runtime
|
||||||
|
* ``main.dart`` — call ``CccCrypto.cccInit()`` at startup
|
||||||
|
* App debug screen — expose ``CccSelfTest.runAll()`` results
|
||||||
|
|
||||||
|
No Rust changes and no bridge changes are made in Milestone 3.
|
||||||
|
|
||||||
|
|
||||||
|
Phase 8 — Stretch Goal Providers (Future)
|
||||||
|
|
||||||
|
*(Out of scope for Phase 4. Tracked here for future scheduling.)*
|
||||||
|
|
||||||
|
================== =====================================================
|
||||||
|
Library Rust crate / approach
|
||||||
|
================== =====================================================
|
||||||
|
libsodium ``sodiumoxide`` or ``safe_libsodium``
|
||||||
|
OpenSSL ``openssl`` crate
|
||||||
|
BoringSSL ``boring`` crate
|
||||||
|
RustCrypto Pure-Rust trait impls; no native dep
|
||||||
|
liboqs Open Quantum Safe — ML-KEM, BIKE, HQC, Falcon,
|
||||||
|
Dilithium, SPHINCS+
|
||||||
|
Signal libsignal ``libsignal`` (Apache-2 subset)
|
||||||
|
Botan ``botan`` crate
|
||||||
|
mbedTLS ``mbedtls`` crate
|
||||||
|
Nettle ``nettle-sys`` crate
|
||||||
|
================== =====================================================
|
||||||
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
==============================================
|
||||||
|
CCC Rust Implementation — Phase Tracking
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
:Last Updated: 2026-02-25
|
||||||
|
|
||||||
|
Legend
|
||||||
|
------
|
||||||
|
|
||||||
|
* ``[ ]`` Not started
|
||||||
|
* ``[~]`` In progress
|
||||||
|
* ``[x]`` Complete
|
||||||
|
* ``[!]`` Blocked
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Three-Milestone Overview
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
============= =================================== ============================
|
||||||
|
Milestone Repository Status
|
||||||
|
============= =================================== ============================
|
||||||
|
**1 (this)** ``ccc_rust`` Complete
|
||||||
|
**2** ``ccc_cryptography`` Not started
|
||||||
|
**3** ``letusmsg`` (existing app) Not started
|
||||||
|
============= =================================== ============================
|
||||||
|
|
||||||
|
Milestone 1 Verification Gate is now passing.
|
||||||
|
Milestone 2 may begin when scheduled.
|
||||||
|
Milestone 3 does not start until the Milestone 2 gate passes.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
Milestone 1 — ``ccc_rust`` Pure Rust Crypto Library
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Phase 1 — Cargo Workspace Scaffold
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
* ``[x]`` Create ``Cargo.toml`` (workspace manifest, 3 members — no bridge crate)
|
||||||
|
* ``[x]`` Create ``rust-toolchain.toml`` (channel = "stable")
|
||||||
|
* ``[x]`` Create ``.cargo/config.toml`` (cross-compile target aliases)
|
||||||
|
* ``[x]`` Create ``vendors/README.md``
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 2 — ``ccc-crypto-core`` Trait Crate
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
* ``[x]`` Create ``crates/ccc-crypto-core/Cargo.toml``
|
||||||
|
* ``[x]`` ``algorithms.rs`` — AeadAlgorithm, KdfAlgorithm, MacAlgorithm,
|
||||||
|
HashAlgorithm, KemAlgorithm enums (values == cipher_constants.dart)
|
||||||
|
* ``[x]`` ``capabilities.rs`` — AlgorithmCapability, ProviderCapabilities
|
||||||
|
* ``[x]`` ``error.rs`` — CryptoError enum
|
||||||
|
* ``[x]`` ``types.rs`` — KemKeyPair, SelfTestReport, BenchmarkReport,
|
||||||
|
AlgoTestResult
|
||||||
|
* ``[x]`` ``provider.rs`` — AeadProvider, KdfProvider, MacProvider,
|
||||||
|
HashProvider, KemProvider traits; CryptoProvider umbrella trait
|
||||||
|
* ``[x]`` ``registry.rs`` — ProviderRegistry (OnceLock<Mutex<...>>),
|
||||||
|
register(), get(), list()
|
||||||
|
* ``[x]`` ``lib.rs`` — re-exports all public items
|
||||||
|
* ``[x]`` Unit tests for registry (5 passing)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 3 — wolfSSL Submodule + ``ccc-crypto-wolfssl``
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
* ``[x]`` ``git submodule add`` wolfSSL → ``vendors/wolfssl``
|
||||||
|
* ``[x]`` Pin submodule to ``v5.7.2-stable``
|
||||||
|
* ``[x]`` Document pin in ``vendors/README.md``
|
||||||
|
* ``[x]`` Create ``crates/ccc-crypto-wolfssl/Cargo.toml``
|
||||||
|
* ``[x]`` ``build.rs`` — cmake build + bindgen; stub_ffi feature bypasses C build
|
||||||
|
* ``[x]`` ``aead.rs`` — AES-256-GCM implementation
|
||||||
|
|
||||||
|
* ``[x]`` encrypt_aead (AES-256-GCM)
|
||||||
|
* ``[x]`` decrypt_aead (AES-256-GCM)
|
||||||
|
* ``[x]`` encrypt_aead (ChaCha20-Poly1305)
|
||||||
|
* ``[x]`` decrypt_aead (ChaCha20-Poly1305)
|
||||||
|
* ``[x]`` encrypt_aead (XChaCha20-Poly1305 via HChaCha20)
|
||||||
|
* ``[x]`` decrypt_aead (XChaCha20-Poly1305)
|
||||||
|
|
||||||
|
* ``[x]`` ``kdf.rs`` — KDF implementations
|
||||||
|
|
||||||
|
* ``[x]`` HKDF-SHA256
|
||||||
|
* ``[x]`` HKDF-SHA384
|
||||||
|
* ``[x]`` HKDF-SHA512
|
||||||
|
* ``[x]`` Argon2id (64 MB / 3 iter / 4 threads — matches DEFAULT_CIPHER_PARAMS)
|
||||||
|
* ``[x]`` BLAKE2b-512 KDF
|
||||||
|
|
||||||
|
* ``[x]`` ``mac.rs`` — MAC implementations
|
||||||
|
|
||||||
|
* ``[x]`` HMAC-SHA256
|
||||||
|
* ``[x]`` HMAC-SHA384
|
||||||
|
* ``[x]`` HMAC-SHA512
|
||||||
|
* ``[x]`` BLAKE2b-MAC (keyed)
|
||||||
|
* ``[x]`` Constant-time verify
|
||||||
|
|
||||||
|
* ``[x]`` ``hash.rs`` — Hash implementations
|
||||||
|
|
||||||
|
* ``[x]`` SHA-256 / SHA-384 / SHA-512
|
||||||
|
* ``[x]`` SHA3-256 / SHA3-512
|
||||||
|
* ``[x]`` BLAKE2b-512
|
||||||
|
|
||||||
|
* ``[x]`` ``kem.rs`` — KEM implementations
|
||||||
|
|
||||||
|
* ``[x]`` X25519 (keygen + DH encap/decap)
|
||||||
|
* ``[x]`` X448 (keygen + DH encap/decap)
|
||||||
|
* ``[ ]`` ML-KEM-768 (deferred to Phase 5)
|
||||||
|
* ``[ ]`` ML-KEM-1024 (deferred to Phase 5)
|
||||||
|
* ``[ ]`` Classic McEliece (deferred to Phase 5)
|
||||||
|
|
||||||
|
* ``[x]`` ``capabilities.rs`` — probe-at-startup per algorithm
|
||||||
|
* ``[x]`` ``capabilities.rs`` — benchmark() throughput micro-bench
|
||||||
|
* ``[x]`` ``provider.rs`` — WolfSslProvider: CryptoProvider impl
|
||||||
|
* ``[x]`` ``provider.rs`` — self_test() with embedded NIST vectors (AES-256-GCM, ChaCha20-Poly1305)
|
||||||
|
* ``[x]`` Register WolfSslProvider in ProviderRegistry via init()
|
||||||
|
* ``[x]`` Full native build verified (cmake builds clean, all conformance tests pass)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 4 — Conformance Test Suite
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
* ``[x]`` NIST AES-256-GCM vectors (2 vectors)
|
||||||
|
* ``[x]`` RFC 8439 ChaCha20-Poly1305 vectors
|
||||||
|
* ``[x]`` RFC 5869 HKDF-SHA256 vectors (2 vectors)
|
||||||
|
* ``[x]`` RFC 4231 HMAC-SHA256 vectors (2 vectors)
|
||||||
|
* ``[x]`` FIPS hash vectors (SHA-256/512, SHA3-256, BLAKE2b-512)
|
||||||
|
* ``[x]`` RFC 7748 X25519 DH test vectors
|
||||||
|
* ``[x]`` RFC 7748 X448 DH test vectors
|
||||||
|
* ``[x]`` XChaCha20-Poly1305 extended-nonce vectors
|
||||||
|
* ``[x]`` ``cargo run -p ccc-conformance-tests`` passes (all current vectors)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 5 — Architecture Documentation
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
* ``[x]`` Create ``docs/ccc_rust_milestone1.rst``
|
||||||
|
* ``[x]`` Crate dependency graph (ASCII diagram)
|
||||||
|
* ``[x]`` "How to add a new provider" — 7-step trait checklist
|
||||||
|
* ``[x]`` ``algo: u32`` → cipher constant mapping table
|
||||||
|
* ``[x]`` Milestone 2 hand-off contract documented
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Milestone 1 Verification Gate
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
*All items must be checked before the* ``v0.1.0`` *tag is cut.*
|
||||||
|
|
||||||
|
* ``[x]`` ``cargo test --workspace`` — all pass
|
||||||
|
* ``[x]`` ``cargo run -p ccc-conformance-tests`` — ALL VECTORS PASSED
|
||||||
|
* ``[x]`` ``cargo build --target aarch64-apple-ios`` — success
|
||||||
|
* ``[x]`` ``cargo build --target aarch64-linux-android`` — success
|
||||||
|
* ``[x]`` No ``flutter_rust_bridge`` / Dart / Flutter dependency in workspace
|
||||||
|
* ``[x]`` ``cargo audit`` — no known CVEs
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
Milestone 2 — ``ccc_cryptography`` Flutter Plugin
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
*(Not started — begins after Milestone 1 gate passes)*
|
||||||
|
|
||||||
|
Phase 1 — New Repository Setup
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Create ``ccc_cryptography`` repository
|
||||||
|
* ``[ ]`` Flutter plugin scaffold (``pubspec.yaml``, ``ios/``, ``android/``, ``macos/``)
|
||||||
|
* ``[ ]`` Rust bridge crate with ``crate-type = ["cdylib", "staticlib"]``
|
||||||
|
* ``[ ]`` Add ``flutter_rust_bridge = "2"`` dependency
|
||||||
|
* ``[ ]`` Reference ``ccc_rust`` via git tag ``v0.1.0``
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 2 — Bridge Crate
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* ``[ ]`` ``dto.rs`` — CapabilitiesDto, KemKeyPairDto, KemEncapDto,
|
||||||
|
SelfTestDto, AlgoTestResultDto; From<core types> impls
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_init()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_list_providers()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_capabilities() / ccc_available_algorithms()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_aead_encrypt() / ccc_aead_decrypt()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_kdf_derive()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_mac_compute() / ccc_mac_verify()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_hash()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_kem_generate_keypair()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_kem_encapsulate() / ccc_kem_decapsulate()
|
||||||
|
* ``[ ]`` ``bridge.rs`` — ccc_self_test()
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 3 — Codegen + Plugin Build
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Run ``flutter_rust_bridge_codegen generate``
|
||||||
|
* ``[ ]`` Verify generated Dart bindings compile
|
||||||
|
* ``[ ]`` ``flutter build ios`` succeeds (static lib linked)
|
||||||
|
* ``[ ]`` ``flutter build apk`` succeeds (cdylib linked)
|
||||||
|
* ``[ ]`` ``flutter build macos`` succeeds
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 4 — Dart API Layer
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` ``CccCrypto`` class (wraps all bridge calls)
|
||||||
|
* ``[ ]`` ``CccSelfTest`` class (wraps ccc_self_test())
|
||||||
|
* ``[ ]`` ``CccProviderCatalog`` (runtime-populated from ccc_capabilities())
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 5 — Flutter Integration Tests
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` Roundtrip encrypt/decrypt 1 KB (AES-256-GCM)
|
||||||
|
* ``[ ]`` Roundtrip encrypt/decrypt 1 KB (ChaCha20-Poly1305)
|
||||||
|
* ``[ ]`` ``CccSelfTest.runAll()`` — all-pass
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Milestone 2 Verification Gate
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
* ``[ ]`` All Flutter integration tests pass on iOS simulator
|
||||||
|
* ``[ ]`` All Flutter integration tests pass on Android emulator
|
||||||
|
* ``[ ]`` Package published / tagged ``v0.1.0``
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
Milestone 3 — LetUsMsg App Integration
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
*(Not started — begins after Milestone 2 gate passes)*
|
||||||
|
|
||||||
|
* ``[ ]`` Add ``ccc_cryptography`` to ``letusmsg`` `pubspec.yaml``
|
||||||
|
* ``[ ]`` Wire ``crypto_wolfssl.dart`` encrypt/decrypt → bridge calls
|
||||||
|
* ``[ ]`` Call ``CccCrypto.cccInit()`` at app startup
|
||||||
|
* ``[ ]`` Populate ``CccProviderCatalog`` from runtime capabilities
|
||||||
|
* ``[ ]`` Expose ``CccSelfTest.runAll()`` in app debug screen
|
||||||
|
* ``[ ]`` End-to-end integration test (send + receive encrypted message)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Phase 8 — Stretch Goal Providers (Future)
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
*(Out of scope for Phase 4. Tracked here for future scheduling.)*
|
||||||
|
|
||||||
|
* ``[ ]`` libsodium (``sodiumoxide`` / ``safe_libsodium``)
|
||||||
|
* ``[ ]`` OpenSSL (``openssl`` crate)
|
||||||
|
* ``[ ]`` BoringSSL (``boring`` crate)
|
||||||
|
* ``[ ]`` RustCrypto (pure-Rust, no native dep)
|
||||||
|
* ``[ ]`` liboqs — ML-KEM, BIKE, HQC, Falcon, Dilithium, SPHINCS+
|
||||||
|
* ``[ ]`` Signal ``libsignal``
|
||||||
|
* ``[ ]`` Botan
|
||||||
|
* ``[ ]`` mbedTLS
|
||||||
|
* ``[ ]`` Nettle
|
||||||
|
|
@ -0,0 +1,414 @@
|
||||||
|
// Integration tests for the CccCrypto public API.
|
||||||
|
//
|
||||||
|
// These exercise every bridge entry point through the clean Dart façade.
|
||||||
|
// All tests require the native library, so they run as integration tests
|
||||||
|
// via `flutter test integration_test/ccc_crypto_test.dart -d macos`.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:ccc_cryptography/ccc_cryptography.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
|
/// Helper: hex string → Uint8List.
|
||||||
|
Uint8List _hex(String hex) {
|
||||||
|
final bytes = <int>[];
|
||||||
|
for (var i = 0; i < hex.length; i += 2) {
|
||||||
|
bytes.add(int.parse(hex.substring(i, i + 2), radix: 16));
|
||||||
|
}
|
||||||
|
return Uint8List.fromList(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper: Uint8List → lowercase hex string.
|
||||||
|
String _toHex(Uint8List data) =>
|
||||||
|
data.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
setUpAll(() async {
|
||||||
|
await CccCrypto.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.1 AEAD roundtrip — AES-256-GCM
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.1 AEAD roundtrip AES-256-GCM', () async {
|
||||||
|
final key = Uint8List(32); // all zeros — fine for a test
|
||||||
|
final nonce = Uint8List(12);
|
||||||
|
final plaintext = Uint8List(1024); // 1 KB of zeros
|
||||||
|
for (var i = 0; i < plaintext.length; i++) {
|
||||||
|
plaintext[i] = i & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ciphertext = await CccCrypto.encryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ciphertext must be longer than plaintext (appended auth tag).
|
||||||
|
expect(ciphertext.length, greaterThan(plaintext.length));
|
||||||
|
|
||||||
|
final decrypted = await CccCrypto.decryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(decrypted, equals(plaintext));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.2 AEAD roundtrip — ChaCha20-Poly1305
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.2 AEAD roundtrip ChaCha20-Poly1305', () async {
|
||||||
|
final key = Uint8List(32);
|
||||||
|
final nonce = Uint8List(12);
|
||||||
|
final plaintext = Uint8List(1024);
|
||||||
|
for (var i = 0; i < plaintext.length; i++) {
|
||||||
|
plaintext[i] = (i * 3) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ciphertext = await CccCrypto.encryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.chaCha20Poly1305,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(ciphertext.length, greaterThan(plaintext.length));
|
||||||
|
|
||||||
|
final decrypted = await CccCrypto.decryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.chaCha20Poly1305,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(decrypted, equals(plaintext));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.3 AEAD tamper test — modifying ciphertext fails authentication
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.3 AEAD tamper test throws CccAuthenticationFailed', () async {
|
||||||
|
final key = Uint8List(32);
|
||||||
|
final nonce = Uint8List(12);
|
||||||
|
final plaintext = Uint8List.fromList(utf8.encode('Hello, tamper test!'));
|
||||||
|
|
||||||
|
final ciphertext = await CccCrypto.encryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Flip a byte in the ciphertext body (not the tag).
|
||||||
|
final tampered = Uint8List.fromList(ciphertext);
|
||||||
|
tampered[0] ^= 0xff;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => CccCrypto.decryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
ciphertext: tampered,
|
||||||
|
),
|
||||||
|
throwsA(isA<CccAuthenticationFailed>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.4 KDF known-vector — HKDF-SHA256 (RFC 5869 Test Case 1)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.4 KDF HKDF-SHA256 RFC 5869 Test Case 1', () async {
|
||||||
|
final ikm = _hex('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b');
|
||||||
|
final salt = _hex('000102030405060708090a0b0c');
|
||||||
|
final info = _hex('f0f1f2f3f4f5f6f7f8f9');
|
||||||
|
const length = 42;
|
||||||
|
final expectedOkm = _hex(
|
||||||
|
'3cb25f25faacd57a90434f64d0362f2a'
|
||||||
|
'2d2d0a90cf1a5a4c5db02d56ecc4c5bf'
|
||||||
|
'34007208d5b887185865',
|
||||||
|
);
|
||||||
|
|
||||||
|
final okm = await CccCrypto.deriveKey(
|
||||||
|
algorithm: CccKdfAlgorithm.sha256,
|
||||||
|
ikm: ikm,
|
||||||
|
salt: salt,
|
||||||
|
info: info,
|
||||||
|
length: length,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(okm.length, equals(length));
|
||||||
|
expect(_toHex(okm), equals(_toHex(expectedOkm)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.5 MAC roundtrip — HMAC-SHA256 compute then verify
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.5 MAC roundtrip HMAC-SHA256', () async {
|
||||||
|
final key = Uint8List(32);
|
||||||
|
for (var i = 0; i < key.length; i++) {
|
||||||
|
key[i] = i & 0xff;
|
||||||
|
}
|
||||||
|
final data = Uint8List.fromList(utf8.encode('MAC roundtrip test data'));
|
||||||
|
|
||||||
|
final mac = await CccCrypto.computeMac(
|
||||||
|
algorithm: CccMacAlgorithm.hmacSha256,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
// HMAC-SHA256 produces 32-byte tag.
|
||||||
|
expect(mac.length, equals(32));
|
||||||
|
|
||||||
|
final valid = await CccCrypto.verifyMac(
|
||||||
|
algorithm: CccMacAlgorithm.hmacSha256,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
mac: mac,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valid, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.6 MAC tamper test — modified data fails verification
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.6 MAC tamper test returns false', () async {
|
||||||
|
final key = Uint8List(32);
|
||||||
|
final data = Uint8List.fromList(utf8.encode('original data'));
|
||||||
|
|
||||||
|
final mac = await CccCrypto.computeMac(
|
||||||
|
algorithm: CccMacAlgorithm.hmacSha256,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Modify the data after MAC was computed.
|
||||||
|
final tamperedData = Uint8List.fromList(utf8.encode('tampered data'));
|
||||||
|
|
||||||
|
final valid = await CccCrypto.verifyMac(
|
||||||
|
algorithm: CccMacAlgorithm.hmacSha256,
|
||||||
|
key: key,
|
||||||
|
data: tamperedData,
|
||||||
|
mac: mac,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valid, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.7 Hash known-vector — SHA-256 / SHA-384 / SHA-512
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.7a Hash SHA-256 known vector', () async {
|
||||||
|
final data = Uint8List.fromList(utf8.encode('abc'));
|
||||||
|
final expected = _hex(
|
||||||
|
'ba7816bf8f01cfea414140de5dae2223'
|
||||||
|
'b00361a396177a9cb410ff61f20015ad',
|
||||||
|
);
|
||||||
|
|
||||||
|
final digest = await CccCrypto.hash(
|
||||||
|
algorithm: CccHashAlgorithm.sha256,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(digest.length, equals(32));
|
||||||
|
expect(_toHex(digest), equals(_toHex(expected)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('5.7b Hash SHA-384 known vector', () async {
|
||||||
|
final data = Uint8List.fromList(utf8.encode('abc'));
|
||||||
|
final expected = _hex(
|
||||||
|
'cb00753f45a35e8bb5a03d699ac65007'
|
||||||
|
'272c32ab0eded1631a8b605a43ff5bed'
|
||||||
|
'8086072ba1e7cc2358baeca134c825a7',
|
||||||
|
);
|
||||||
|
|
||||||
|
final digest = await CccCrypto.hash(
|
||||||
|
algorithm: CccHashAlgorithm.sha384,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(digest.length, equals(48));
|
||||||
|
expect(_toHex(digest), equals(_toHex(expected)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('5.7c Hash SHA-512 known vector', () async {
|
||||||
|
final data = Uint8List.fromList(utf8.encode('abc'));
|
||||||
|
final expected = _hex(
|
||||||
|
'ddaf35a193617abacc417349ae204131'
|
||||||
|
'12e6fa4e89a97ea20a9eeee64b55d39a'
|
||||||
|
'2192992a274fc1a836ba3c23a3feebbd'
|
||||||
|
'454d4423643ce80e2a9ac94fa54ca49f',
|
||||||
|
);
|
||||||
|
|
||||||
|
final digest = await CccCrypto.hash(
|
||||||
|
algorithm: CccHashAlgorithm.sha512,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(digest.length, equals(64));
|
||||||
|
expect(_toHex(digest), equals(_toHex(expected)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.8 KEM roundtrip — X25519
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.8 KEM roundtrip X25519', () async {
|
||||||
|
final kp = await CccCrypto.kemGenerateKeypair(
|
||||||
|
algorithm: CccKemAlgorithm.x25519,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(kp.publicKey.length, equals(32));
|
||||||
|
expect(kp.privateKey.length, equals(32));
|
||||||
|
|
||||||
|
final encap = await CccCrypto.kemEncapsulate(
|
||||||
|
algorithm: CccKemAlgorithm.x25519,
|
||||||
|
publicKey: kp.publicKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(encap.sharedSecret.length, equals(32));
|
||||||
|
expect(encap.ciphertext.length, equals(32));
|
||||||
|
|
||||||
|
final decapSecret = await CccCrypto.kemDecapsulate(
|
||||||
|
algorithm: CccKemAlgorithm.x25519,
|
||||||
|
privateKey: kp.privateKey,
|
||||||
|
ciphertext: encap.ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(decapSecret, equals(encap.sharedSecret));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.9 KEM roundtrip — X448
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.9 KEM roundtrip X448', () async {
|
||||||
|
final kp = await CccCrypto.kemGenerateKeypair(
|
||||||
|
algorithm: CccKemAlgorithm.x448,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(kp.publicKey.length, equals(56));
|
||||||
|
expect(kp.privateKey.length, equals(56));
|
||||||
|
|
||||||
|
final encap = await CccCrypto.kemEncapsulate(
|
||||||
|
algorithm: CccKemAlgorithm.x448,
|
||||||
|
publicKey: kp.publicKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(encap.sharedSecret.length, equals(56));
|
||||||
|
expect(encap.ciphertext.length, equals(56));
|
||||||
|
|
||||||
|
final decapSecret = await CccCrypto.kemDecapsulate(
|
||||||
|
algorithm: CccKemAlgorithm.x448,
|
||||||
|
privateKey: kp.privateKey,
|
||||||
|
ciphertext: encap.ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(decapSecret, equals(encap.sharedSecret));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.10 Provider catalog
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.10 Provider catalog', () async {
|
||||||
|
final providers = CccCrypto.listProviders();
|
||||||
|
expect(providers, isNotEmpty);
|
||||||
|
expect(providers, contains('wolfssl'));
|
||||||
|
|
||||||
|
final catalog = await CccCrypto.getCapabilities();
|
||||||
|
expect(catalog.providerName, equals('wolfssl'));
|
||||||
|
|
||||||
|
// Each category should have at least one algorithm.
|
||||||
|
expect(catalog.aead, isNotEmpty);
|
||||||
|
expect(catalog.kdf, isNotEmpty);
|
||||||
|
expect(catalog.mac, isNotEmpty);
|
||||||
|
expect(catalog.hash, isNotEmpty);
|
||||||
|
expect(catalog.kem, isNotEmpty);
|
||||||
|
|
||||||
|
// Check that availability and scores are populated.
|
||||||
|
for (final algo in catalog.availableAead) {
|
||||||
|
expect(algo.name, isNotEmpty);
|
||||||
|
expect(algo.efficiencyScore, greaterThanOrEqualTo(0));
|
||||||
|
expect(algo.reliabilityScore, greaterThanOrEqualTo(0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.11 Self-test validation
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.11 Self-test validation', () async {
|
||||||
|
final result = await CccCrypto.runSelfTest();
|
||||||
|
|
||||||
|
expect(result.providerName, equals('wolfssl'));
|
||||||
|
expect(result.results, isNotEmpty);
|
||||||
|
expect(result.allPassed, isTrue,
|
||||||
|
reason: 'Failures: ${result.failures.map((r) => '${r.algoName}: ${r.errorMessage}').join(', ')}');
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.12 Error handling — typed exceptions
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.12a Error: invalid key length throws CccInvalidKey', () async {
|
||||||
|
// AES-256-GCM requires 32-byte key; provide 16 bytes.
|
||||||
|
final shortKey = Uint8List(16);
|
||||||
|
final nonce = Uint8List(12);
|
||||||
|
final plaintext = Uint8List.fromList(utf8.encode('test'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => CccCrypto.encryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: shortKey,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
),
|
||||||
|
throwsA(isA<CccException>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('5.12b Error: invalid nonce length throws CccException', () async {
|
||||||
|
final key = Uint8List(32);
|
||||||
|
// AES-GCM nonce should be 12 bytes; provide 8.
|
||||||
|
final shortNonce = Uint8List(8);
|
||||||
|
final plaintext = Uint8List.fromList(utf8.encode('test'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => CccCrypto.encryptAead(
|
||||||
|
algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
key: key,
|
||||||
|
nonce: shortNonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
),
|
||||||
|
throwsA(isA<CccException>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// 5.13 Algorithm ID mapping — enum discriminants match Rust repr(u32)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
test('5.13 Algorithm ID mapping', () async {
|
||||||
|
// Verify the catalog reports algorithm IDs matching Rust enum discriminants.
|
||||||
|
final catalog = await CccCrypto.getCapabilities();
|
||||||
|
|
||||||
|
// AEAD: AesGcm256 = 12, ChaCha20Poly1305 = 13, XChaCha20Poly1305 = 14
|
||||||
|
final aeadIds = {for (final a in catalog.aead) a.name: a.id};
|
||||||
|
expect(aeadIds['AES-256-GCM'], equals(12));
|
||||||
|
expect(aeadIds['ChaCha20-Poly1305'], equals(13));
|
||||||
|
|
||||||
|
// Hash: Sha256 = 40, Sha384 = 41, Sha512 = 42
|
||||||
|
final hashIds = {for (final a in catalog.hash) a.name: a.id};
|
||||||
|
expect(hashIds['SHA-256'], equals(40));
|
||||||
|
expect(hashIds['SHA-384'], equals(41));
|
||||||
|
expect(hashIds['SHA-512'], equals(42));
|
||||||
|
|
||||||
|
// KEM: X25519 = 50, X448 = 51
|
||||||
|
final kemIds = {for (final a in catalog.kem) a.name: a.id};
|
||||||
|
expect(kemIds['X25519'], equals(50));
|
||||||
|
expect(kemIds['X448'], equals(51));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
B6F5CBF28A5AED1D91C67990 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA91E11C618D9A599FFAA263 /* Pods_RunnerTests.framework */; };
|
||||||
|
EDAD8E7F06F64A81B3A5C0C2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FED151958504C181ACC8B7C1 /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
|
@ -40,14 +42,17 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
10458A559A1BAA9A40196C6D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
4AF37E1D71E0F25D8BA5F8FD /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
906E8CF8E280ABC053ECC0A2 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
|
@ -55,6 +60,11 @@
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
CA91E11C618D9A599FFAA263 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D47ACED1129408D2F3AF66D0 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
DA2CEF79122E98943D4DD5F3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
E28B19D3AE6076D6F92EDB36 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
FED151958504C181ACC8B7C1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
|
@ -62,6 +72,15 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
EDAD8E7F06F64A81B3A5C0C2 /* Pods_Runner.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
C16C09A3B0FC34A2C018419D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
B6F5CBF28A5AED1D91C67990 /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -76,6 +95,15 @@
|
||||||
path = RunnerTests;
|
path = RunnerTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3411DCCD962964321A55906E /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FED151958504C181ACC8B7C1 /* Pods_Runner.framework */,
|
||||||
|
CA91E11C618D9A599FFAA263 /* Pods_RunnerTests.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -94,6 +122,8 @@
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
B594E4F1914A4F3EB9181D75 /* Pods */,
|
||||||
|
3411DCCD962964321A55906E /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
|
@ -121,6 +151,20 @@
|
||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
B594E4F1914A4F3EB9181D75 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
10458A559A1BAA9A40196C6D /* Pods-Runner.debug.xcconfig */,
|
||||||
|
E28B19D3AE6076D6F92EDB36 /* Pods-Runner.release.xcconfig */,
|
||||||
|
DA2CEF79122E98943D4DD5F3 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
4AF37E1D71E0F25D8BA5F8FD /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
906E8CF8E280ABC053ECC0A2 /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
D47ACED1129408D2F3AF66D0 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
|
@ -128,8 +172,10 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
53A6DF3E8C78EC92FF484B14 /* [CP] Check Pods Manifest.lock */,
|
||||||
331C807D294A63A400263BE5 /* Sources */,
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
331C807F294A63A400263BE5 /* Resources */,
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
C16C09A3B0FC34A2C018419D /* Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -145,12 +191,14 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
77834BA3CCE54739B52F93C3 /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
C9725E95439E143BF1E75A7A /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -238,6 +286,50 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
53A6DF3E8C78EC92FF484B14 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
77834BA3CCE54739B52F93C3 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
|
|
@ -253,6 +345,23 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
|
C9725E95439E143BF1E75A7A /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|
@ -379,6 +488,7 @@
|
||||||
};
|
};
|
||||||
331C8088294A63A400263BE5 /* Debug */ = {
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 4AF37E1D71E0F25D8BA5F8FD /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
|
@ -396,6 +506,7 @@
|
||||||
};
|
};
|
||||||
331C8089294A63A400263BE5 /* Release */ = {
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 906E8CF8E280ABC053ECC0A2 /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
|
@ -411,6 +522,7 @@
|
||||||
};
|
};
|
||||||
331C808A294A63A400263BE5 /* Profile */ = {
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = D47ACED1129408D2F3AF66D0 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,7 @@
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:async';
|
import 'package:ccc_cryptography/ccc_cryptography.dart';
|
||||||
|
|
||||||
import 'package:ccc_cryptography/ccc_cryptography.dart' as ccc_cryptography;
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
void main() {
|
await CccCrypto.init();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,58 +15,36 @@ class MyApp extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyAppState extends State<MyApp> {
|
class _MyAppState extends State<MyApp> {
|
||||||
late int sumResult;
|
String _status = 'Running self-test…';
|
||||||
late Future<int> sumAsyncResult;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
sumResult = ccc_cryptography.sum(1, 2);
|
_runSelfTest();
|
||||||
sumAsyncResult = ccc_cryptography.sumAsync(3, 4);
|
}
|
||||||
|
|
||||||
|
Future<void> _runSelfTest() async {
|
||||||
|
try {
|
||||||
|
final result = await CccCrypto.runSelfTest();
|
||||||
|
final providers = CccCrypto.listProviders();
|
||||||
|
setState(() {
|
||||||
|
_status = 'Providers: ${providers.join(", ")}\n'
|
||||||
|
'Self-test: ${result.allPassed ? "ALL PASSED" : "FAILED"}\n'
|
||||||
|
'${result.results.map((r) => ' ${r.algoName}: ${r.passed ? "✓" : "✗ ${r.errorMessage}"}').join("\n")}';
|
||||||
|
});
|
||||||
|
} on CccException catch (e) {
|
||||||
|
setState(() => _status = 'Error: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const textStyle = TextStyle(fontSize: 25);
|
|
||||||
const spacerSmall = SizedBox(height: 10);
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('ccc_cryptography')),
|
||||||
title: const Text('Native Packages'),
|
body: Padding(
|
||||||
),
|
padding: const EdgeInsets.all(16),
|
||||||
body: SingleChildScrollView(
|
child: Text(_status, style: const TextStyle(fontSize: 16)),
|
||||||
child: Container(
|
|
||||||
padding: const .all(10),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'This calls a native function through FFI that is shipped as source in the package. '
|
|
||||||
'The native code is built as part of the Flutter Runner build.',
|
|
||||||
style: textStyle,
|
|
||||||
textAlign: .center,
|
|
||||||
),
|
|
||||||
spacerSmall,
|
|
||||||
Text(
|
|
||||||
'sum(1, 2) = $sumResult',
|
|
||||||
style: textStyle,
|
|
||||||
textAlign: .center,
|
|
||||||
),
|
|
||||||
spacerSmall,
|
|
||||||
FutureBuilder<int>(
|
|
||||||
future: sumAsyncResult,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<int> value) {
|
|
||||||
final displayValue =
|
|
||||||
(value.hasData) ? value.data : 'loading';
|
|
||||||
return Text(
|
|
||||||
'await sumAsync(3, 4) = $displayValue',
|
|
||||||
style: textStyle,
|
|
||||||
textAlign: .center,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
platform :osx, '10.15'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_macos_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_macos_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||||
|
C9D9D80953306779817A01D7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 038972E62E6D13C2A55A0B7F /* Pods_Runner.framework */; };
|
||||||
|
CC6FDE117C14C0F98D1BA8BB /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5AA6C665A590100A722C2D2 /* Pods_RunnerTests.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
|
@ -60,11 +62,13 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
038972E62E6D13C2A55A0B7F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
0988C826A2FD255462F9808A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* ccc_cryptography_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ccc_cryptography_example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* ccc_cryptography_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ccc_cryptography_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
|
@ -77,7 +81,13 @@
|
||||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
86D70DA30699567E710A0459 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9D35F14235952B671983F7E0 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
A5B81D3E19756CB2D0C8B009 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
A81A44EB7B90A16EAADE4FA6 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
BA626D8003BA6E48536EE306 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D5AA6C665A590100A722C2D2 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
|
@ -85,6 +95,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
CC6FDE117C14C0F98D1BA8BB /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -92,6 +103,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
C9D9D80953306779817A01D7 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -125,6 +137,7 @@
|
||||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||||
33CC10EE2044A3C60003C045 /* Products */,
|
33CC10EE2044A3C60003C045 /* Products */,
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||||
|
DE0D4D416823266B5CAAEDA7 /* Pods */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
|
@ -175,10 +188,26 @@
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
038972E62E6D13C2A55A0B7F /* Pods_Runner.framework */,
|
||||||
|
D5AA6C665A590100A722C2D2 /* Pods_RunnerTests.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
DE0D4D416823266B5CAAEDA7 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0988C826A2FD255462F9808A /* Pods-Runner.debug.xcconfig */,
|
||||||
|
86D70DA30699567E710A0459 /* Pods-Runner.release.xcconfig */,
|
||||||
|
BA626D8003BA6E48536EE306 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
A81A44EB7B90A16EAADE4FA6 /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
9D35F14235952B671983F7E0 /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
A5B81D3E19756CB2D0C8B009 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
|
@ -186,6 +215,7 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
086D907707E0A9BB81BA038A /* [CP] Check Pods Manifest.lock */,
|
||||||
331C80D1294CF70F00263BE5 /* Sources */,
|
331C80D1294CF70F00263BE5 /* Sources */,
|
||||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||||
331C80D3294CF70F00263BE5 /* Resources */,
|
331C80D3294CF70F00263BE5 /* Resources */,
|
||||||
|
|
@ -204,11 +234,13 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
C86250B37FA1453AAEC9A1C2 /* [CP] Check Pods Manifest.lock */,
|
||||||
33CC10E92044A3C60003C045 /* Sources */,
|
33CC10E92044A3C60003C045 /* Sources */,
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
|
78F7E7C7FE2E75BBB3819847 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -291,6 +323,28 @@
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
086D907707E0A9BB81BA038A /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
|
|
@ -329,6 +383,45 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||||
};
|
};
|
||||||
|
78F7E7C7FE2E75BBB3819847 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
C86250B37FA1453AAEC9A1C2 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|
@ -380,6 +473,7 @@
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = A81A44EB7B90A16EAADE4FA6 /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
|
@ -394,6 +488,7 @@
|
||||||
};
|
};
|
||||||
331C80DC294CF71000263BE5 /* Release */ = {
|
331C80DC294CF71000263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9D35F14235952B671983F7E0 /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
|
@ -408,6 +503,7 @@
|
||||||
};
|
};
|
||||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = A5B81D3E19756CB2D0C8B009 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,7 @@
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ dev_dependencies:
|
||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import 'package:integration_test/integration_test_driver.dart';
|
||||||
|
|
||||||
|
Future<void> main() => integrationDriver();
|
||||||
19
ffigen.yaml
19
ffigen.yaml
|
|
@ -1,19 +0,0 @@
|
||||||
# Run with `dart run ffigen --config ffigen.yaml`.
|
|
||||||
name: CccCryptographyBindings
|
|
||||||
description: |
|
|
||||||
Bindings for `src/ccc_cryptography.h`.
|
|
||||||
|
|
||||||
Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
|
|
||||||
output: 'lib/ccc_cryptography_bindings_generated.dart'
|
|
||||||
headers:
|
|
||||||
entry-points:
|
|
||||||
- 'src/ccc_cryptography.h'
|
|
||||||
include-directives:
|
|
||||||
- 'src/ccc_cryptography.h'
|
|
||||||
preamble: |
|
|
||||||
// ignore_for_file: always_specify_types
|
|
||||||
// ignore_for_file: camel_case_types
|
|
||||||
// ignore_for_file: non_constant_identifier_names
|
|
||||||
comments:
|
|
||||||
style: any
|
|
||||||
length: full
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
rust_input: crate::api
|
||||||
|
dart_output: lib/src/rust
|
||||||
|
rust_root: rust/
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
// Relative import to be able to reuse the C sources.
|
|
||||||
// See the comment in ../ccc_cryptography.podspec for more information.
|
|
||||||
#include "../../src/ccc_cryptography.c"
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
// This is an empty file to force CocoaPods to create a framework.
|
||||||
|
|
@ -20,9 +20,26 @@ A new Flutter FFI plugin project.
|
||||||
s.source = { :path => '.' }
|
s.source = { :path => '.' }
|
||||||
s.source_files = 'Classes/**/*'
|
s.source_files = 'Classes/**/*'
|
||||||
s.dependency 'Flutter'
|
s.dependency 'Flutter'
|
||||||
s.platform = :ios, '13.0'
|
s.platform = :ios, '11.0'
|
||||||
|
|
||||||
# Flutter.framework does not contain a i386 slice.
|
# Flutter.framework does not contain a i386 slice.
|
||||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||||
s.swift_version = '5.0'
|
s.swift_version = '5.0'
|
||||||
end
|
|
||||||
|
s.script_phase = {
|
||||||
|
:name => 'Build Rust library',
|
||||||
|
# First argument is relative path to the `rust` folder, second is name of rust library
|
||||||
|
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust ccc_cryptography',
|
||||||
|
:execution_position => :before_compile,
|
||||||
|
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
|
||||||
|
# Let XCode know that the static library referenced in -force_load below is
|
||||||
|
# created by this build step.
|
||||||
|
:output_files => ["${BUILT_PRODUCTS_DIR}/libccc_cryptography.a"],
|
||||||
|
}
|
||||||
|
s.pod_target_xcconfig = {
|
||||||
|
'DEFINES_MODULE' => 'YES',
|
||||||
|
# Flutter.framework does not contain a i386 slice.
|
||||||
|
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||||
|
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libccc_cryptography.a',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
/// Primary entry point for CCC cryptographic operations.
|
||||||
|
///
|
||||||
|
/// All methods delegate to the Rust bridge via `flutter_rust_bridge`.
|
||||||
|
/// No cryptographic logic exists in Dart.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// ```dart
|
||||||
|
/// await CccCrypto.init();
|
||||||
|
/// final ct = await CccCrypto.encryptAead(
|
||||||
|
/// algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
|
/// key: key,
|
||||||
|
/// nonce: nonce,
|
||||||
|
/// plaintext: data,
|
||||||
|
/// aad: Uint8List(0),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:ccc_cryptography/ccc_exceptions.dart';
|
||||||
|
import 'package:ccc_cryptography/ccc_provider_catalog.dart';
|
||||||
|
import 'package:ccc_cryptography/ccc_self_test.dart';
|
||||||
|
import 'package:ccc_cryptography/src/rust/api/crypto.dart' as bridge;
|
||||||
|
import 'package:ccc_cryptography/src/rust/api/dto.dart';
|
||||||
|
import 'package:ccc_cryptography/src/rust/frb_generated.dart';
|
||||||
|
|
||||||
|
/// Static façade for all CCC cryptographic operations.
|
||||||
|
///
|
||||||
|
/// Call [init] once at application startup before using any other method.
|
||||||
|
abstract final class CccCrypto {
|
||||||
|
/// Initialise the FRB runtime and register the wolfSSL provider.
|
||||||
|
///
|
||||||
|
/// Must be called once before any other [CccCrypto] method.
|
||||||
|
/// Safe to call multiple times (idempotent).
|
||||||
|
static Future<void> init() async {
|
||||||
|
await RustLib.init();
|
||||||
|
await bridge.cccInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Provider info ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Return the names of all registered crypto providers.
|
||||||
|
static List<String> listProviders() => bridge.cccListProviders();
|
||||||
|
|
||||||
|
/// Return the capability catalog for the default provider.
|
||||||
|
static Future<CccProviderCatalog> getCapabilities() async {
|
||||||
|
try {
|
||||||
|
final caps = await bridge.cccCapabilities();
|
||||||
|
return CccProviderCatalog.fromCapabilities(caps);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AEAD ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Encrypt [plaintext] with an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Returns ciphertext with appended authentication tag.
|
||||||
|
static Future<Uint8List> encryptAead({
|
||||||
|
required CccAeadAlgorithm algorithm,
|
||||||
|
required Uint8List key,
|
||||||
|
required Uint8List nonce,
|
||||||
|
required Uint8List plaintext,
|
||||||
|
Uint8List? aad,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccAeadEncrypt(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
aad: aad ?? Uint8List(0),
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt [ciphertext] (with appended tag) using an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Throws [CccAuthenticationFailed] if the tag does not verify.
|
||||||
|
static Future<Uint8List> decryptAead({
|
||||||
|
required CccAeadAlgorithm algorithm,
|
||||||
|
required Uint8List key,
|
||||||
|
required Uint8List nonce,
|
||||||
|
required Uint8List ciphertext,
|
||||||
|
Uint8List? aad,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccAeadDecrypt(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
aad: aad ?? Uint8List(0),
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── KDF ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Derive key material of [length] bytes.
|
||||||
|
static Future<Uint8List> deriveKey({
|
||||||
|
required CccKdfAlgorithm algorithm,
|
||||||
|
required Uint8List ikm,
|
||||||
|
Uint8List? salt,
|
||||||
|
Uint8List? info,
|
||||||
|
required int length,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccKdfDerive(
|
||||||
|
algorithm: algorithm,
|
||||||
|
ikm: ikm,
|
||||||
|
salt: salt ?? Uint8List(0),
|
||||||
|
info: info ?? Uint8List(0),
|
||||||
|
length: length,
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── MAC ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Compute a MAC tag over [data].
|
||||||
|
static Future<Uint8List> computeMac({
|
||||||
|
required CccMacAlgorithm algorithm,
|
||||||
|
required Uint8List key,
|
||||||
|
required Uint8List data,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccMacCompute(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify a MAC tag. Returns `true` if valid, `false` otherwise.
|
||||||
|
static Future<bool> verifyMac({
|
||||||
|
required CccMacAlgorithm algorithm,
|
||||||
|
required Uint8List key,
|
||||||
|
required Uint8List data,
|
||||||
|
required Uint8List mac,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccMacVerify(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
mac: mac,
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hash ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Compute a cryptographic hash of [data].
|
||||||
|
static Future<Uint8List> hash({
|
||||||
|
required CccHashAlgorithm algorithm,
|
||||||
|
required Uint8List data,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccHash(algorithm: algorithm, data: data);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── KEM ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generate a KEM key pair.
|
||||||
|
static Future<CccKemKeyPair> kemGenerateKeypair({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccKemGenerateKeypair(algorithm: algorithm);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM encapsulation — produce ciphertext + shared secret from a public key.
|
||||||
|
static Future<CccKemEncapResult> kemEncapsulate({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
required Uint8List publicKey,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccKemEncapsulate(
|
||||||
|
algorithm: algorithm,
|
||||||
|
publicKey: publicKey,
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM decapsulation — recover shared secret from ciphertext + private key.
|
||||||
|
static Future<Uint8List> kemDecapsulate({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
required Uint8List privateKey,
|
||||||
|
required Uint8List ciphertext,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await bridge.cccKemDecapsulate(
|
||||||
|
algorithm: algorithm,
|
||||||
|
privateKey: privateKey,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Self-test ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Run the provider self-test suite and return a structured report.
|
||||||
|
static Future<CccSelfTestResult> runSelfTest() async {
|
||||||
|
try {
|
||||||
|
final report = await bridge.cccSelfTest();
|
||||||
|
return CccSelfTestResult.fromReport(report);
|
||||||
|
} on CccCryptoError catch (e) {
|
||||||
|
throw CccException.from(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,131 +1,39 @@
|
||||||
|
/// CCC Cryptography — Flutter plugin providing cross-platform
|
||||||
import 'dart:async';
|
/// cryptographic operations via `ccc_rust` and `flutter_rust_bridge`.
|
||||||
import 'dart:ffi';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:isolate';
|
|
||||||
|
|
||||||
import 'ccc_cryptography_bindings_generated.dart';
|
|
||||||
|
|
||||||
/// A very short-lived native function.
|
|
||||||
///
|
///
|
||||||
/// For very short-lived functions, it is fine to call them on the main isolate.
|
/// Usage:
|
||||||
/// They will block the Dart execution while running the native function, so
|
/// ```dart
|
||||||
/// only do this for native functions which are guaranteed to be short-lived.
|
/// import 'package:ccc_cryptography/ccc_cryptography.dart';
|
||||||
int sum(int a, int b) => _bindings.sum(a, b);
|
|
||||||
|
|
||||||
/// A longer lived native function, which occupies the thread calling it.
|
|
||||||
///
|
///
|
||||||
/// Do not call these kind of native functions in the main isolate. They will
|
/// await CccCrypto.init();
|
||||||
/// block Dart execution. This will cause dropped frames in Flutter applications.
|
/// final ct = await CccCrypto.encryptAead(
|
||||||
/// Instead, call these native functions on a separate isolate.
|
/// algorithm: CccAeadAlgorithm.aesGcm256,
|
||||||
///
|
/// key: key, nonce: nonce, plaintext: data,
|
||||||
/// Modify this to suit your own use case. Example use cases:
|
/// );
|
||||||
///
|
/// ```
|
||||||
/// 1. Reuse a single isolate for various different kinds of requests.
|
library;
|
||||||
/// 2. Use multiple helper isolates for parallel execution.
|
|
||||||
Future<int> sumAsync(int a, int b) async {
|
|
||||||
final SendPort helperIsolateSendPort = await _helperIsolateSendPort;
|
|
||||||
final int requestId = _nextSumRequestId++;
|
|
||||||
final _SumRequest request = _SumRequest(requestId, a, b);
|
|
||||||
final Completer<int> completer = Completer<int>();
|
|
||||||
_sumRequests[requestId] = completer;
|
|
||||||
helperIsolateSendPort.send(request);
|
|
||||||
return completer.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
const String _libName = 'ccc_cryptography';
|
// Primary API
|
||||||
|
export 'ccc_crypto.dart';
|
||||||
|
|
||||||
/// The dynamic library in which the symbols for [CccCryptographyBindings] can be found.
|
// Algorithm enum re-exports (FRB-generated)
|
||||||
final DynamicLibrary _dylib = () {
|
export 'src/rust/api/dto.dart'
|
||||||
if (Platform.isMacOS || Platform.isIOS) {
|
show
|
||||||
return DynamicLibrary.open('$_libName.framework/$_libName');
|
CccAeadAlgorithm,
|
||||||
}
|
CccKdfAlgorithm,
|
||||||
if (Platform.isAndroid || Platform.isLinux) {
|
CccMacAlgorithm,
|
||||||
return DynamicLibrary.open('lib$_libName.so');
|
CccHashAlgorithm,
|
||||||
}
|
CccKemAlgorithm,
|
||||||
if (Platform.isWindows) {
|
CccKemKeyPair,
|
||||||
return DynamicLibrary.open('$_libName.dll');
|
CccKemEncapResult;
|
||||||
}
|
|
||||||
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
|
|
||||||
}();
|
|
||||||
|
|
||||||
/// The bindings to the native functions in [_dylib].
|
// Exceptions
|
||||||
final CccCryptographyBindings _bindings = CccCryptographyBindings(_dylib);
|
export 'ccc_exceptions.dart';
|
||||||
|
|
||||||
|
// Provider catalog
|
||||||
|
export 'ccc_provider_catalog.dart';
|
||||||
|
|
||||||
|
// Self-test result
|
||||||
|
export 'ccc_self_test.dart';
|
||||||
|
|
||||||
|
|
||||||
/// A request to compute `sum`.
|
|
||||||
///
|
|
||||||
/// Typically sent from one isolate to another.
|
|
||||||
class _SumRequest {
|
|
||||||
final int id;
|
|
||||||
final int a;
|
|
||||||
final int b;
|
|
||||||
|
|
||||||
const _SumRequest(this.id, this.a, this.b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A response with the result of `sum`.
|
|
||||||
///
|
|
||||||
/// Typically sent from one isolate to another.
|
|
||||||
class _SumResponse {
|
|
||||||
final int id;
|
|
||||||
final int result;
|
|
||||||
|
|
||||||
const _SumResponse(this.id, this.result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Counter to identify [_SumRequest]s and [_SumResponse]s.
|
|
||||||
int _nextSumRequestId = 0;
|
|
||||||
|
|
||||||
/// Mapping from [_SumRequest] `id`s to the completers corresponding to the correct future of the pending request.
|
|
||||||
final Map<int, Completer<int>> _sumRequests = <int, Completer<int>>{};
|
|
||||||
|
|
||||||
/// The SendPort belonging to the helper isolate.
|
|
||||||
Future<SendPort> _helperIsolateSendPort = () async {
|
|
||||||
// The helper isolate is going to send us back a SendPort, which we want to
|
|
||||||
// wait for.
|
|
||||||
final Completer<SendPort> completer = Completer<SendPort>();
|
|
||||||
|
|
||||||
// Receive port on the main isolate to receive messages from the helper.
|
|
||||||
// We receive two types of messages:
|
|
||||||
// 1. A port to send messages on.
|
|
||||||
// 2. Responses to requests we sent.
|
|
||||||
final ReceivePort receivePort = ReceivePort()
|
|
||||||
..listen((dynamic data) {
|
|
||||||
if (data is SendPort) {
|
|
||||||
// The helper isolate sent us the port on which we can sent it requests.
|
|
||||||
completer.complete(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data is _SumResponse) {
|
|
||||||
// The helper isolate sent us a response to a request we sent.
|
|
||||||
final Completer<int> completer = _sumRequests[data.id]!;
|
|
||||||
_sumRequests.remove(data.id);
|
|
||||||
completer.complete(data.result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the helper isolate.
|
|
||||||
await Isolate.spawn((SendPort sendPort) async {
|
|
||||||
final ReceivePort helperReceivePort = ReceivePort()
|
|
||||||
..listen((dynamic data) {
|
|
||||||
// On the helper isolate listen to requests and respond to them.
|
|
||||||
if (data is _SumRequest) {
|
|
||||||
final int result = _bindings.sum_long_running(data.a, data.b);
|
|
||||||
final _SumResponse response = _SumResponse(data.id, result);
|
|
||||||
sendPort.send(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send the port to the main isolate on which we can receive requests.
|
|
||||||
sendPort.send(helperReceivePort.sendPort);
|
|
||||||
}, receivePort.sendPort);
|
|
||||||
|
|
||||||
// Wait until the helper isolate has sent us back the SendPort on which we
|
|
||||||
// can start sending requests.
|
|
||||||
return completer.future;
|
|
||||||
}();
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
// ignore_for_file: always_specify_types
|
|
||||||
// ignore_for_file: camel_case_types
|
|
||||||
// ignore_for_file: non_constant_identifier_names
|
|
||||||
|
|
||||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
|
||||||
//
|
|
||||||
// Generated by `package:ffigen`.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'dart:ffi' as ffi;
|
|
||||||
|
|
||||||
/// Bindings for `src/ccc_cryptography.h`.
|
|
||||||
///
|
|
||||||
/// Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
|
|
||||||
///
|
|
||||||
class CccCryptographyBindings {
|
|
||||||
/// Holds the symbol lookup function.
|
|
||||||
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
|
||||||
_lookup;
|
|
||||||
|
|
||||||
/// The symbols are looked up in [dynamicLibrary].
|
|
||||||
CccCryptographyBindings(ffi.DynamicLibrary dynamicLibrary)
|
|
||||||
: _lookup = dynamicLibrary.lookup;
|
|
||||||
|
|
||||||
/// The symbols are looked up with [lookup].
|
|
||||||
CccCryptographyBindings.fromLookup(
|
|
||||||
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
|
||||||
lookup)
|
|
||||||
: _lookup = lookup;
|
|
||||||
|
|
||||||
/// A very short-lived native function.
|
|
||||||
///
|
|
||||||
/// For very short-lived functions, it is fine to call them on the main isolate.
|
|
||||||
/// They will block the Dart execution while running the native function, so
|
|
||||||
/// only do this for native functions which are guaranteed to be short-lived.
|
|
||||||
int sum(
|
|
||||||
int a,
|
|
||||||
int b,
|
|
||||||
) {
|
|
||||||
return _sum(
|
|
||||||
a,
|
|
||||||
b,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
late final _sumPtr =
|
|
||||||
_lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>('sum');
|
|
||||||
late final _sum = _sumPtr.asFunction<int Function(int, int)>();
|
|
||||||
|
|
||||||
/// A longer lived native function, which occupies the thread calling it.
|
|
||||||
///
|
|
||||||
/// Do not call these kind of native functions in the main isolate. They will
|
|
||||||
/// block Dart execution. This will cause dropped frames in Flutter applications.
|
|
||||||
/// Instead, call these native functions on a separate isolate.
|
|
||||||
int sum_long_running(
|
|
||||||
int a,
|
|
||||||
int b,
|
|
||||||
) {
|
|
||||||
return _sum_long_running(
|
|
||||||
a,
|
|
||||||
b,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
late final _sum_long_runningPtr =
|
|
||||||
_lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>(
|
|
||||||
'sum_long_running');
|
|
||||||
late final _sum_long_running =
|
|
||||||
_sum_long_runningPtr.asFunction<int Function(int, int)>();
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/// Structured exception classes for CCC cryptographic operations.
|
||||||
|
///
|
||||||
|
/// These wrap the FRB-generated [CccCryptoError] into typed Dart exceptions
|
||||||
|
/// so consuming code can catch specific failure modes.
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:ccc_cryptography/src/rust/api/dto.dart';
|
||||||
|
|
||||||
|
/// Base class for all CCC cryptographic exceptions.
|
||||||
|
sealed class CccException implements Exception {
|
||||||
|
/// Human-readable error description.
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const CccException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => '$runtimeType: $message';
|
||||||
|
|
||||||
|
/// Convert a FRB-generated [CccCryptoError] into the appropriate
|
||||||
|
/// typed [CccException] subclass.
|
||||||
|
factory CccException.from(CccCryptoError error) {
|
||||||
|
return switch (error) {
|
||||||
|
CccCryptoError_UnsupportedAlgorithm(:final field0) =>
|
||||||
|
CccUnsupportedAlgorithm(field0),
|
||||||
|
CccCryptoError_InvalidKey(:final field0) => CccInvalidKey(field0),
|
||||||
|
CccCryptoError_InvalidNonce(:final field0) => CccInvalidNonce(field0),
|
||||||
|
CccCryptoError_AuthenticationFailed() =>
|
||||||
|
const CccAuthenticationFailed(),
|
||||||
|
CccCryptoError_InvalidInput(:final field0) => CccInvalidInput(field0),
|
||||||
|
CccCryptoError_FeatureNotCompiled(:final field0) =>
|
||||||
|
CccFeatureNotCompiled(field0),
|
||||||
|
CccCryptoError_InternalError(:final field0) => CccInternalError(field0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The requested algorithm is not supported by any registered provider.
|
||||||
|
class CccUnsupportedAlgorithm extends CccException {
|
||||||
|
const CccUnsupportedAlgorithm(super.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The supplied key is invalid (wrong length, weak key, etc.).
|
||||||
|
class CccInvalidKey extends CccException {
|
||||||
|
const CccInvalidKey(super.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The supplied nonce/IV is invalid (wrong length, reuse detected, etc.).
|
||||||
|
class CccInvalidNonce extends CccException {
|
||||||
|
const CccInvalidNonce(super.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AEAD authentication tag verification failed — ciphertext was tampered.
|
||||||
|
class CccAuthenticationFailed extends CccException {
|
||||||
|
const CccAuthenticationFailed() : super('authentication failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The supplied input data is invalid.
|
||||||
|
class CccInvalidInput extends CccException {
|
||||||
|
const CccInvalidInput(super.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The requested feature/algorithm is not compiled into the provider.
|
||||||
|
class CccFeatureNotCompiled extends CccException {
|
||||||
|
const CccFeatureNotCompiled(super.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unexpected internal error occurred in the cryptographic provider.
|
||||||
|
class CccInternalError extends CccException {
|
||||||
|
const CccInternalError(super.message);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
/// Provider catalog — describes what a registered provider supports.
|
||||||
|
///
|
||||||
|
/// Wraps the FRB-generated [CccCapabilities] into a Dart-idiomatic type
|
||||||
|
/// for querying algorithm availability and quality scores.
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:ccc_cryptography/src/rust/api/dto.dart';
|
||||||
|
|
||||||
|
/// Describes the full set of capabilities for a registered provider.
|
||||||
|
class CccProviderCatalog {
|
||||||
|
/// Provider name (e.g. "wolfssl").
|
||||||
|
final String providerName;
|
||||||
|
|
||||||
|
/// AEAD algorithms supported by this provider.
|
||||||
|
final List<CccAlgorithmInfo> aead;
|
||||||
|
|
||||||
|
/// KDF algorithms supported by this provider.
|
||||||
|
final List<CccAlgorithmInfo> kdf;
|
||||||
|
|
||||||
|
/// MAC algorithms supported by this provider.
|
||||||
|
final List<CccAlgorithmInfo> mac;
|
||||||
|
|
||||||
|
/// Hash algorithms supported by this provider.
|
||||||
|
final List<CccAlgorithmInfo> hash;
|
||||||
|
|
||||||
|
/// KEM algorithms supported by this provider.
|
||||||
|
final List<CccAlgorithmInfo> kem;
|
||||||
|
|
||||||
|
const CccProviderCatalog._({
|
||||||
|
required this.providerName,
|
||||||
|
required this.aead,
|
||||||
|
required this.kdf,
|
||||||
|
required this.mac,
|
||||||
|
required this.hash,
|
||||||
|
required this.kem,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Convert from the FRB-generated capabilities DTO.
|
||||||
|
factory CccProviderCatalog.fromCapabilities(CccCapabilities caps) {
|
||||||
|
return CccProviderCatalog._(
|
||||||
|
providerName: caps.providerName,
|
||||||
|
aead: caps.aead.map(CccAlgorithmInfo._fromEntry).toList(growable: false),
|
||||||
|
kdf: caps.kdf.map(CccAlgorithmInfo._fromEntry).toList(growable: false),
|
||||||
|
mac: caps.mac.map(CccAlgorithmInfo._fromEntry).toList(growable: false),
|
||||||
|
hash: caps.hash.map(CccAlgorithmInfo._fromEntry).toList(growable: false),
|
||||||
|
kem: caps.kem.map(CccAlgorithmInfo._fromEntry).toList(growable: false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All available AEAD algorithms.
|
||||||
|
List<CccAlgorithmInfo> get availableAead =>
|
||||||
|
aead.where((a) => a.available).toList(growable: false);
|
||||||
|
|
||||||
|
/// All available KDF algorithms.
|
||||||
|
List<CccAlgorithmInfo> get availableKdf =>
|
||||||
|
kdf.where((a) => a.available).toList(growable: false);
|
||||||
|
|
||||||
|
/// All available MAC algorithms.
|
||||||
|
List<CccAlgorithmInfo> get availableMac =>
|
||||||
|
mac.where((a) => a.available).toList(growable: false);
|
||||||
|
|
||||||
|
/// All available Hash algorithms.
|
||||||
|
List<CccAlgorithmInfo> get availableHash =>
|
||||||
|
hash.where((a) => a.available).toList(growable: false);
|
||||||
|
|
||||||
|
/// All available KEM algorithms.
|
||||||
|
List<CccAlgorithmInfo> get availableKem =>
|
||||||
|
kem.where((a) => a.available).toList(growable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes a single algorithm's availability and quality metrics.
|
||||||
|
class CccAlgorithmInfo {
|
||||||
|
/// Numeric algorithm identifier (matches Rust `#[repr(u32)]`).
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
/// Human-readable algorithm name (e.g. "AES-256-GCM").
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Whether this algorithm is available in the provider.
|
||||||
|
final bool available;
|
||||||
|
|
||||||
|
/// Whether the algorithm produces deterministic output for the same input.
|
||||||
|
final bool deterministicIo;
|
||||||
|
|
||||||
|
/// Provider-reported efficiency score (0–100).
|
||||||
|
final int efficiencyScore;
|
||||||
|
|
||||||
|
/// Provider-reported reliability score (0–100).
|
||||||
|
final int reliabilityScore;
|
||||||
|
|
||||||
|
const CccAlgorithmInfo({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.available,
|
||||||
|
required this.deterministicIo,
|
||||||
|
required this.efficiencyScore,
|
||||||
|
required this.reliabilityScore,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CccAlgorithmInfo._fromEntry(CccAlgorithmEntry e) {
|
||||||
|
return CccAlgorithmInfo(
|
||||||
|
id: e.algoId,
|
||||||
|
name: e.algoName,
|
||||||
|
available: e.capability.available,
|
||||||
|
deterministicIo: e.capability.deterministicIo,
|
||||||
|
efficiencyScore: e.capability.efficiencyScore,
|
||||||
|
reliabilityScore: e.capability.reliabilityScore,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/// Self-test result — wraps the FRB-generated report into Dart-idiomatic types.
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:ccc_cryptography/src/rust/api/dto.dart';
|
||||||
|
|
||||||
|
/// Result of a single algorithm's self-test.
|
||||||
|
class CccAlgorithmTestResult {
|
||||||
|
/// Numeric algorithm identifier.
|
||||||
|
final int algoId;
|
||||||
|
|
||||||
|
/// Human-readable algorithm name.
|
||||||
|
final String algoName;
|
||||||
|
|
||||||
|
/// Whether the algorithm passed its self-test.
|
||||||
|
final bool passed;
|
||||||
|
|
||||||
|
/// Diagnostic message if the test failed; `null` on success.
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
|
const CccAlgorithmTestResult({
|
||||||
|
required this.algoId,
|
||||||
|
required this.algoName,
|
||||||
|
required this.passed,
|
||||||
|
this.errorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CccAlgorithmTestResult._fromDto(CccAlgoTestResult dto) {
|
||||||
|
return CccAlgorithmTestResult(
|
||||||
|
algoId: dto.algoId,
|
||||||
|
algoName: dto.algoName,
|
||||||
|
passed: dto.passed,
|
||||||
|
errorMessage: dto.errorMessage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aggregate result of the provider self-test.
|
||||||
|
class CccSelfTestResult {
|
||||||
|
/// Name of the provider that was tested.
|
||||||
|
final String providerName;
|
||||||
|
|
||||||
|
/// Per-algorithm test results.
|
||||||
|
final List<CccAlgorithmTestResult> results;
|
||||||
|
|
||||||
|
/// Whether all algorithms passed.
|
||||||
|
final bool allPassed;
|
||||||
|
|
||||||
|
const CccSelfTestResult._({
|
||||||
|
required this.providerName,
|
||||||
|
required this.results,
|
||||||
|
required this.allPassed,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Convert from the FRB-generated self-test report DTO.
|
||||||
|
factory CccSelfTestResult.fromReport(CccSelfTestReport report) {
|
||||||
|
return CccSelfTestResult._(
|
||||||
|
providerName: report.providerName,
|
||||||
|
results: report.results
|
||||||
|
.map(CccAlgorithmTestResult._fromDto)
|
||||||
|
.toList(growable: false),
|
||||||
|
allPassed: report.allPassed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Algorithms that failed their self-test.
|
||||||
|
List<CccAlgorithmTestResult> get failures =>
|
||||||
|
results.where((r) => !r.passed).toList(growable: false);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../frb_generated.dart';
|
||||||
|
import 'dto.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
|
// These functions are ignored because they are not marked as `pub`: `default_provider`, `kem_provider`
|
||||||
|
|
||||||
|
/// Initialise the CCC cryptographic subsystem.
|
||||||
|
///
|
||||||
|
/// Registers the wolfSSL provider in the global registry.
|
||||||
|
/// Safe to call multiple times (idempotent).
|
||||||
|
Future<void> cccInit() => RustLib.instance.api.crateApiCryptoCccInit();
|
||||||
|
|
||||||
|
/// List all registered provider names.
|
||||||
|
List<String> cccListProviders() =>
|
||||||
|
RustLib.instance.api.crateApiCryptoCccListProviders();
|
||||||
|
|
||||||
|
/// Return the capabilities of the default provider.
|
||||||
|
Future<CccCapabilities> cccCapabilities() =>
|
||||||
|
RustLib.instance.api.crateApiCryptoCccCapabilities();
|
||||||
|
|
||||||
|
/// Encrypt with an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Returns ciphertext‖tag.
|
||||||
|
Future<Uint8List> cccAeadEncrypt({
|
||||||
|
required CccAeadAlgorithm algorithm,
|
||||||
|
required List<int> key,
|
||||||
|
required List<int> nonce,
|
||||||
|
required List<int> plaintext,
|
||||||
|
required List<int> aad,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccAeadEncrypt(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
plaintext: plaintext,
|
||||||
|
aad: aad,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Decrypt with an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Expects ciphertext‖tag as `ciphertext`.
|
||||||
|
Future<Uint8List> cccAeadDecrypt({
|
||||||
|
required CccAeadAlgorithm algorithm,
|
||||||
|
required List<int> key,
|
||||||
|
required List<int> nonce,
|
||||||
|
required List<int> ciphertext,
|
||||||
|
required List<int> aad,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccAeadDecrypt(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
nonce: nonce,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
aad: aad,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Derive key material using a KDF.
|
||||||
|
Future<Uint8List> cccKdfDerive({
|
||||||
|
required CccKdfAlgorithm algorithm,
|
||||||
|
required List<int> ikm,
|
||||||
|
required List<int> salt,
|
||||||
|
required List<int> info,
|
||||||
|
required int length,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccKdfDerive(
|
||||||
|
algorithm: algorithm,
|
||||||
|
ikm: ikm,
|
||||||
|
salt: salt,
|
||||||
|
info: info,
|
||||||
|
length: length,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Compute a MAC tag.
|
||||||
|
Future<Uint8List> cccMacCompute({
|
||||||
|
required CccMacAlgorithm algorithm,
|
||||||
|
required List<int> key,
|
||||||
|
required List<int> data,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccMacCompute(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Verify a MAC tag. Returns `true` if valid.
|
||||||
|
Future<bool> cccMacVerify({
|
||||||
|
required CccMacAlgorithm algorithm,
|
||||||
|
required List<int> key,
|
||||||
|
required List<int> data,
|
||||||
|
required List<int> mac,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccMacVerify(
|
||||||
|
algorithm: algorithm,
|
||||||
|
key: key,
|
||||||
|
data: data,
|
||||||
|
mac: mac,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Compute a cryptographic hash.
|
||||||
|
Future<Uint8List> cccHash({
|
||||||
|
required CccHashAlgorithm algorithm,
|
||||||
|
required List<int> data,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccHash(
|
||||||
|
algorithm: algorithm,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Generate a KEM key pair.
|
||||||
|
Future<CccKemKeyPair> cccKemGenerateKeypair({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccKemGenerateKeypair(
|
||||||
|
algorithm: algorithm,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// KEM encapsulation — produce ciphertext + shared secret from a public key.
|
||||||
|
Future<CccKemEncapResult> cccKemEncapsulate({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
required List<int> publicKey,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccKemEncapsulate(
|
||||||
|
algorithm: algorithm,
|
||||||
|
publicKey: publicKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// KEM decapsulation — recover shared secret from ciphertext + private key.
|
||||||
|
Future<Uint8List> cccKemDecapsulate({
|
||||||
|
required CccKemAlgorithm algorithm,
|
||||||
|
required List<int> privateKey,
|
||||||
|
required List<int> ciphertext,
|
||||||
|
}) => RustLib.instance.api.crateApiCryptoCccKemDecapsulate(
|
||||||
|
algorithm: algorithm,
|
||||||
|
privateKey: privateKey,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Run the provider self-test and return a structured report.
|
||||||
|
Future<CccSelfTestReport> cccSelfTest() =>
|
||||||
|
RustLib.instance.api.crateApiCryptoCccSelfTest();
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||||
|
part 'dto.freezed.dart';
|
||||||
|
|
||||||
|
// These functions are ignored because they are not marked as `pub`: `to_core`, `to_core`, `to_core`, `to_core`, `to_core`
|
||||||
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
|
||||||
|
|
||||||
|
/// AEAD algorithm identifiers — values match `ccc_rust` `#[repr(u32)]`.
|
||||||
|
enum CccAeadAlgorithm {
|
||||||
|
aesGcm256,
|
||||||
|
chaCha20Poly1305,
|
||||||
|
xChaCha20Poly1305,
|
||||||
|
ascon128A,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per-algorithm self-test result.
|
||||||
|
class CccAlgoTestResult {
|
||||||
|
final int algoId;
|
||||||
|
final String algoName;
|
||||||
|
final bool passed;
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
|
const CccAlgoTestResult({
|
||||||
|
required this.algoId,
|
||||||
|
required this.algoName,
|
||||||
|
required this.passed,
|
||||||
|
this.errorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
algoId.hashCode ^
|
||||||
|
algoName.hashCode ^
|
||||||
|
passed.hashCode ^
|
||||||
|
errorMessage.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccAlgoTestResult &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
algoId == other.algoId &&
|
||||||
|
algoName == other.algoName &&
|
||||||
|
passed == other.passed &&
|
||||||
|
errorMessage == other.errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per-algorithm availability and quality scores.
|
||||||
|
class CccAlgorithmCapability {
|
||||||
|
final bool available;
|
||||||
|
final bool deterministicIo;
|
||||||
|
final int efficiencyScore;
|
||||||
|
final int reliabilityScore;
|
||||||
|
|
||||||
|
const CccAlgorithmCapability({
|
||||||
|
required this.available,
|
||||||
|
required this.deterministicIo,
|
||||||
|
required this.efficiencyScore,
|
||||||
|
required this.reliabilityScore,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
available.hashCode ^
|
||||||
|
deterministicIo.hashCode ^
|
||||||
|
efficiencyScore.hashCode ^
|
||||||
|
reliabilityScore.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccAlgorithmCapability &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
available == other.available &&
|
||||||
|
deterministicIo == other.deterministicIo &&
|
||||||
|
efficiencyScore == other.efficiencyScore &&
|
||||||
|
reliabilityScore == other.reliabilityScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An algorithm entry in the capabilities list.
|
||||||
|
class CccAlgorithmEntry {
|
||||||
|
final int algoId;
|
||||||
|
final String algoName;
|
||||||
|
final CccAlgorithmCapability capability;
|
||||||
|
|
||||||
|
const CccAlgorithmEntry({
|
||||||
|
required this.algoId,
|
||||||
|
required this.algoName,
|
||||||
|
required this.capability,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => algoId.hashCode ^ algoName.hashCode ^ capability.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccAlgorithmEntry &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
algoId == other.algoId &&
|
||||||
|
algoName == other.algoName &&
|
||||||
|
capability == other.capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provider capabilities — flattened from HashMap to Vec for FRB.
|
||||||
|
class CccCapabilities {
|
||||||
|
final String providerName;
|
||||||
|
final List<CccAlgorithmEntry> aead;
|
||||||
|
final List<CccAlgorithmEntry> kdf;
|
||||||
|
final List<CccAlgorithmEntry> mac;
|
||||||
|
final List<CccAlgorithmEntry> hash;
|
||||||
|
final List<CccAlgorithmEntry> kem;
|
||||||
|
|
||||||
|
const CccCapabilities({
|
||||||
|
required this.providerName,
|
||||||
|
required this.aead,
|
||||||
|
required this.kdf,
|
||||||
|
required this.mac,
|
||||||
|
required this.hash,
|
||||||
|
required this.kem,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
providerName.hashCode ^
|
||||||
|
aead.hashCode ^
|
||||||
|
kdf.hashCode ^
|
||||||
|
mac.hashCode ^
|
||||||
|
hash.hashCode ^
|
||||||
|
kem.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccCapabilities &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
providerName == other.providerName &&
|
||||||
|
aead == other.aead &&
|
||||||
|
kdf == other.kdf &&
|
||||||
|
mac == other.mac &&
|
||||||
|
hash == other.hash &&
|
||||||
|
kem == other.kem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class CccCryptoError with _$CccCryptoError implements FrbException {
|
||||||
|
const CccCryptoError._();
|
||||||
|
|
||||||
|
const factory CccCryptoError.unsupportedAlgorithm(String field0) =
|
||||||
|
CccCryptoError_UnsupportedAlgorithm;
|
||||||
|
const factory CccCryptoError.invalidKey(String field0) =
|
||||||
|
CccCryptoError_InvalidKey;
|
||||||
|
const factory CccCryptoError.invalidNonce(String field0) =
|
||||||
|
CccCryptoError_InvalidNonce;
|
||||||
|
const factory CccCryptoError.authenticationFailed() =
|
||||||
|
CccCryptoError_AuthenticationFailed;
|
||||||
|
const factory CccCryptoError.invalidInput(String field0) =
|
||||||
|
CccCryptoError_InvalidInput;
|
||||||
|
const factory CccCryptoError.featureNotCompiled(String field0) =
|
||||||
|
CccCryptoError_FeatureNotCompiled;
|
||||||
|
const factory CccCryptoError.internalError(String field0) =
|
||||||
|
CccCryptoError_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hash algorithm identifiers.
|
||||||
|
enum CccHashAlgorithm { sha256, sha384, sha512, blake2B512, sha3256, sha3512 }
|
||||||
|
|
||||||
|
/// KDF algorithm identifiers.
|
||||||
|
enum CccKdfAlgorithm { sha256, sha384, sha512, blake2B512, argon2Id, kmac256 }
|
||||||
|
|
||||||
|
/// KEM algorithm identifiers.
|
||||||
|
enum CccKemAlgorithm {
|
||||||
|
x25519,
|
||||||
|
x448,
|
||||||
|
mlKem768,
|
||||||
|
mlKem1024,
|
||||||
|
classicMcEliece460896,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM encapsulation result — ciphertext + shared secret.
|
||||||
|
class CccKemEncapResult {
|
||||||
|
final Uint8List ciphertext;
|
||||||
|
final Uint8List sharedSecret;
|
||||||
|
|
||||||
|
const CccKemEncapResult({
|
||||||
|
required this.ciphertext,
|
||||||
|
required this.sharedSecret,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => ciphertext.hashCode ^ sharedSecret.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccKemEncapResult &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
ciphertext == other.ciphertext &&
|
||||||
|
sharedSecret == other.sharedSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM key pair — public + private key bytes.
|
||||||
|
class CccKemKeyPair {
|
||||||
|
final Uint8List publicKey;
|
||||||
|
final Uint8List privateKey;
|
||||||
|
|
||||||
|
const CccKemKeyPair({required this.publicKey, required this.privateKey});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => publicKey.hashCode ^ privateKey.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccKemKeyPair &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
publicKey == other.publicKey &&
|
||||||
|
privateKey == other.privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MAC algorithm identifiers.
|
||||||
|
enum CccMacAlgorithm {
|
||||||
|
hmacSha256,
|
||||||
|
hmacSha384,
|
||||||
|
hmacSha512,
|
||||||
|
blake2BMac,
|
||||||
|
poly1305,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aggregate self-test report.
|
||||||
|
class CccSelfTestReport {
|
||||||
|
final String providerName;
|
||||||
|
final List<CccAlgoTestResult> results;
|
||||||
|
final bool allPassed;
|
||||||
|
|
||||||
|
const CccSelfTestReport({
|
||||||
|
required this.providerName,
|
||||||
|
required this.results,
|
||||||
|
required this.allPassed,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
providerName.hashCode ^ results.hashCode ^ allPassed.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CccSelfTestReport &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
providerName == other.providerName &&
|
||||||
|
results == other.results &&
|
||||||
|
allPassed == other.allPassed;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,10 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
|
String greet({required String name}) =>
|
||||||
|
RustLib.instance.api.crateApiSimpleGreet(name: name);
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,324 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||||
|
|
||||||
|
import 'api/crypto.dart';
|
||||||
|
import 'api/dto.dart';
|
||||||
|
import 'api/simple.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:ffi' as ffi;
|
||||||
|
import 'frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
||||||
|
|
||||||
|
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
|
RustLibApiImplPlatform({
|
||||||
|
required super.handler,
|
||||||
|
required super.wire,
|
||||||
|
required super.generalizedFrbRustBinding,
|
||||||
|
required super.portManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String dco_decode_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
bool dco_decode_bool(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAeadAlgorithm dco_decode_ccc_aead_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgoTestResult dco_decode_ccc_algo_test_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmCapability dco_decode_ccc_algorithm_capability(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmEntry dco_decode_ccc_algorithm_entry(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCapabilities dco_decode_ccc_capabilities(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCryptoError dco_decode_ccc_crypto_error(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccHashAlgorithm dco_decode_ccc_hash_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKdfAlgorithm dco_decode_ccc_kdf_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemAlgorithm dco_decode_ccc_kem_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemEncapResult dco_decode_ccc_kem_encap_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemKeyPair dco_decode_ccc_kem_key_pair(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccMacAlgorithm dco_decode_ccc_mac_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccSelfTestReport dco_decode_ccc_self_test_report(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_i_32(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<String> dco_decode_list_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgoTestResult> dco_decode_list_ccc_algo_test_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgorithmEntry> dco_decode_list_ccc_algorithm_entry(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String? dco_decode_opt_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_u_32(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_u_8(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void dco_decode_unit(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String sse_decode_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
bool sse_decode_bool(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAeadAlgorithm sse_decode_ccc_aead_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgoTestResult sse_decode_ccc_algo_test_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmCapability sse_decode_ccc_algorithm_capability(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmEntry sse_decode_ccc_algorithm_entry(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCapabilities sse_decode_ccc_capabilities(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCryptoError sse_decode_ccc_crypto_error(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccHashAlgorithm sse_decode_ccc_hash_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKdfAlgorithm sse_decode_ccc_kdf_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemAlgorithm sse_decode_ccc_kem_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemEncapResult sse_decode_ccc_kem_encap_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemKeyPair sse_decode_ccc_kem_key_pair(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccMacAlgorithm sse_decode_ccc_mac_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccSelfTestReport sse_decode_ccc_self_test_report(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_i_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgoTestResult> sse_decode_list_ccc_algo_test_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgorithmEntry> sse_decode_list_ccc_algorithm_entry(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String? sse_decode_opt_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_u_8(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_decode_unit(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_String(String self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_aead_algorithm(
|
||||||
|
CccAeadAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algo_test_result(
|
||||||
|
CccAlgoTestResult self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algorithm_capability(
|
||||||
|
CccAlgorithmCapability self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algorithm_entry(
|
||||||
|
CccAlgorithmEntry self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_capabilities(
|
||||||
|
CccCapabilities self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_crypto_error(
|
||||||
|
CccCryptoError self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_hash_algorithm(
|
||||||
|
CccHashAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kdf_algorithm(
|
||||||
|
CccKdfAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_algorithm(
|
||||||
|
CccKemAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_encap_result(
|
||||||
|
CccKemEncapResult self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_key_pair(
|
||||||
|
CccKemKeyPair self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_mac_algorithm(
|
||||||
|
CccMacAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_self_test_report(
|
||||||
|
CccSelfTestReport self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_ccc_algo_test_result(
|
||||||
|
List<CccAlgoTestResult> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_ccc_algorithm_entry(
|
||||||
|
List<CccAlgorithmEntry> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_prim_u_8_strict(
|
||||||
|
Uint8List self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_opt_String(String? self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_u_8(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_unit(void self, SseSerializer serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section: wire_class
|
||||||
|
|
||||||
|
class RustLibWire implements BaseWire {
|
||||||
|
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
|
||||||
|
RustLibWire(lib.ffiDynamicLibrary);
|
||||||
|
|
||||||
|
/// Holds the symbol lookup function.
|
||||||
|
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
||||||
|
_lookup;
|
||||||
|
|
||||||
|
/// The symbols are looked up in [dynamicLibrary].
|
||||||
|
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
||||||
|
: _lookup = dynamicLibrary.lookup;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,324 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||||
|
|
||||||
|
// Static analysis wrongly picks the IO variant, thus ignore this
|
||||||
|
// ignore_for_file: argument_type_not_assignable
|
||||||
|
|
||||||
|
import 'api/crypto.dart';
|
||||||
|
import 'api/dto.dart';
|
||||||
|
import 'api/simple.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
|
||||||
|
|
||||||
|
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
|
RustLibApiImplPlatform({
|
||||||
|
required super.handler,
|
||||||
|
required super.wire,
|
||||||
|
required super.generalizedFrbRustBinding,
|
||||||
|
required super.portManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String dco_decode_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
bool dco_decode_bool(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAeadAlgorithm dco_decode_ccc_aead_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgoTestResult dco_decode_ccc_algo_test_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmCapability dco_decode_ccc_algorithm_capability(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmEntry dco_decode_ccc_algorithm_entry(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCapabilities dco_decode_ccc_capabilities(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCryptoError dco_decode_ccc_crypto_error(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccHashAlgorithm dco_decode_ccc_hash_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKdfAlgorithm dco_decode_ccc_kdf_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemAlgorithm dco_decode_ccc_kem_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemEncapResult dco_decode_ccc_kem_encap_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemKeyPair dco_decode_ccc_kem_key_pair(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccMacAlgorithm dco_decode_ccc_mac_algorithm(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccSelfTestReport dco_decode_ccc_self_test_report(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_i_32(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<String> dco_decode_list_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgoTestResult> dco_decode_list_ccc_algo_test_result(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgorithmEntry> dco_decode_list_ccc_algorithm_entry(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String? dco_decode_opt_String(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_u_32(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_u_8(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void dco_decode_unit(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String sse_decode_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
bool sse_decode_bool(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAeadAlgorithm sse_decode_ccc_aead_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgoTestResult sse_decode_ccc_algo_test_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmCapability sse_decode_ccc_algorithm_capability(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccAlgorithmEntry sse_decode_ccc_algorithm_entry(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCapabilities sse_decode_ccc_capabilities(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccCryptoError sse_decode_ccc_crypto_error(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccHashAlgorithm sse_decode_ccc_hash_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKdfAlgorithm sse_decode_ccc_kdf_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemAlgorithm sse_decode_ccc_kem_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemEncapResult sse_decode_ccc_kem_encap_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccKemKeyPair sse_decode_ccc_kem_key_pair(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccMacAlgorithm sse_decode_ccc_mac_algorithm(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
CccSelfTestReport sse_decode_ccc_self_test_report(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_i_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgoTestResult> sse_decode_list_ccc_algo_test_result(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<CccAlgorithmEntry> sse_decode_list_ccc_algorithm_entry(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
String? sse_decode_opt_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_u_8(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_decode_unit(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_String(String self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_aead_algorithm(
|
||||||
|
CccAeadAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algo_test_result(
|
||||||
|
CccAlgoTestResult self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algorithm_capability(
|
||||||
|
CccAlgorithmCapability self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_algorithm_entry(
|
||||||
|
CccAlgorithmEntry self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_capabilities(
|
||||||
|
CccCapabilities self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_crypto_error(
|
||||||
|
CccCryptoError self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_hash_algorithm(
|
||||||
|
CccHashAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kdf_algorithm(
|
||||||
|
CccKdfAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_algorithm(
|
||||||
|
CccKemAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_encap_result(
|
||||||
|
CccKemEncapResult self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_kem_key_pair(
|
||||||
|
CccKemKeyPair self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_mac_algorithm(
|
||||||
|
CccMacAlgorithm self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_ccc_self_test_report(
|
||||||
|
CccSelfTestReport self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_ccc_algo_test_result(
|
||||||
|
List<CccAlgoTestResult> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_ccc_algorithm_entry(
|
||||||
|
List<CccAlgorithmEntry> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_prim_u_8_strict(
|
||||||
|
Uint8List self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_opt_String(String? self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_u_8(int self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_unit(void self, SseSerializer serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section: wire_class
|
||||||
|
|
||||||
|
class RustLibWire implements BaseWire {
|
||||||
|
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JS('wasm_bindgen')
|
||||||
|
external RustLibWasmModule get wasmModule;
|
||||||
|
|
||||||
|
@JS()
|
||||||
|
@anonymous
|
||||||
|
extension type RustLibWasmModule._(JSObject _) implements JSObject {}
|
||||||
|
|
@ -7,16 +7,13 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
set(PROJECT_NAME "ccc_cryptography")
|
set(PROJECT_NAME "ccc_cryptography")
|
||||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||||
|
|
||||||
# Invoke the build for native code shared with the other target platforms.
|
include("../cargokit/cmake/cargokit.cmake")
|
||||||
# This can be changed to accommodate different builds.
|
apply_cargokit(${PROJECT_NAME} ../rust ccc_cryptography "")
|
||||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
|
|
||||||
|
|
||||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||||
# This list could contain prebuilt libraries, or libraries created by an
|
# This list could contain prebuilt libraries, or libraries created by an
|
||||||
# external build triggered from this build file.
|
# external build triggered from this build file.
|
||||||
set(ccc_cryptography_bundled_libraries
|
set(ccc_cryptography_bundled_libraries
|
||||||
# Defined in ../src/CMakeLists.txt.
|
"${${PROJECT_NAME}_cargokit_lib}"
|
||||||
# This can be changed to accommodate different builds.
|
|
||||||
$<TARGET_FILE:ccc_cryptography>
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
// Relative import to be able to reuse the C sources.
|
|
||||||
// See the comment in ../ccc_cryptography.podspec for more information.
|
|
||||||
#include "../../src/ccc_cryptography.c"
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
// This is an empty file to force CocoaPods to create a framework.
|
||||||
|
|
@ -18,17 +18,27 @@ A new Flutter FFI plugin project.
|
||||||
# paths, so Classes contains a forwarder C file that relatively imports
|
# paths, so Classes contains a forwarder C file that relatively imports
|
||||||
# `../src/*` so that the C sources can be shared among all target platforms.
|
# `../src/*` so that the C sources can be shared among all target platforms.
|
||||||
s.source = { :path => '.' }
|
s.source = { :path => '.' }
|
||||||
s.source_files = 'Classes/**/*'
|
s.source_files = 'Classes/**/*'
|
||||||
|
|
||||||
# If your plugin requires a privacy manifest, for example if it collects user
|
|
||||||
# data, update the PrivacyInfo.xcprivacy file to describe your plugin's
|
|
||||||
# privacy impact, and then uncomment this line. For more information,
|
|
||||||
# see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
|
|
||||||
# s.resource_bundles = {'ccc_cryptography_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
|
|
||||||
|
|
||||||
s.dependency 'FlutterMacOS'
|
s.dependency 'FlutterMacOS'
|
||||||
|
|
||||||
s.platform = :osx, '10.11'
|
s.platform = :osx, '10.11'
|
||||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||||
s.swift_version = '5.0'
|
s.swift_version = '5.0'
|
||||||
end
|
|
||||||
|
s.script_phase = {
|
||||||
|
:name => 'Build Rust library',
|
||||||
|
# First argument is relative path to the `rust` folder, second is name of rust library
|
||||||
|
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust ccc_cryptography',
|
||||||
|
:execution_position => :before_compile,
|
||||||
|
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
|
||||||
|
# Let XCode know that the static library referenced in -force_load below is
|
||||||
|
# created by this build step.
|
||||||
|
:output_files => ["${BUILT_PRODUCTS_DIR}/libccc_cryptography.a"],
|
||||||
|
}
|
||||||
|
s.pod_target_xcconfig = {
|
||||||
|
'DEFINES_MODULE' => 'YES',
|
||||||
|
# Flutter.framework does not contain a i386 slice.
|
||||||
|
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||||
|
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libccc_cryptography.a',
|
||||||
|
}
|
||||||
|
end
|
||||||
10
pubspec.yaml
10
pubspec.yaml
|
|
@ -1,7 +1,7 @@
|
||||||
name: ccc_cryptography
|
name: ccc_cryptography
|
||||||
description: "Copius Cipher Chain Cryptography"
|
description: "Copius Cipher Chain Cryptography"
|
||||||
|
publish_to: 'none'
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
homepage:
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.10.1
|
sdk: ^3.10.1
|
||||||
|
|
@ -11,13 +11,19 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
plugin_platform_interface: ^2.0.2
|
plugin_platform_interface: ^2.0.2
|
||||||
|
flutter_rust_bridge: 2.11.1
|
||||||
|
freezed_annotation: ^2.4.1
|
||||||
|
meta: ^1.11.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
ffi: ^2.1.3
|
ffi: ^2.1.3
|
||||||
ffigen: ^13.0.0
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
|
build_runner: ^2.4.8
|
||||||
|
freezed: ^2.5.2
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
plugin:
|
plugin:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "ccc_cryptography"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dev paths — switch to git deps for CI/release:
|
||||||
|
# ccc-crypto-core = { git = "ssh://git@10.0.5.109/j3g/lum_ccc_rust.git", rev = "b1873b2" }
|
||||||
|
# ccc-crypto-wolfssl = { git = "ssh://git@10.0.5.109/j3g/lum_ccc_rust.git", rev = "b1873b2" }
|
||||||
|
ccc-crypto-core = { path = "/Volumes/LUM/source/letusmsg_proj/app/lum_ccc_rust/crates/ccc-crypto-core" }
|
||||||
|
ccc-crypto-wolfssl = { path = "/Volumes/LUM/source/letusmsg_proj/app/lum_ccc_rust/crates/ccc-crypto-wolfssl" }
|
||||||
|
flutter_rust_bridge = "=2.11.1"
|
||||||
|
log = "0.4"
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
// Bridge API — thin wrappers that delegate to ccc_rust providers.
|
||||||
|
//
|
||||||
|
// No cryptographic logic here; only type conversions and registry lookups.
|
||||||
|
|
||||||
|
use ccc_crypto_core::{KemProvider, ProviderRegistry};
|
||||||
|
use ccc_crypto_wolfssl::WolfSslProvider;
|
||||||
|
|
||||||
|
use crate::api::dto::*;
|
||||||
|
|
||||||
|
/// Trace-level bridge logging. Visible only when `RUST_LOG=trace` (or
|
||||||
|
/// equivalent) is set; compiled out in release builds when the `log` crate's
|
||||||
|
/// `release_max_level_off` feature is enabled.
|
||||||
|
macro_rules! dbg_bridge {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
log::trace!("[ccc-bridge] {}", format!($($arg)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Default provider name used when the caller doesn't specify one.
|
||||||
|
const DEFAULT_PROVIDER: &str = "wolfssl";
|
||||||
|
|
||||||
|
/// Get the default provider from the registry or return an error.
|
||||||
|
fn default_provider(
|
||||||
|
) -> Result<std::sync::Arc<dyn ccc_crypto_core::provider::CryptoProvider>, CccCryptoError> {
|
||||||
|
ProviderRegistry::global()
|
||||||
|
.get(DEFAULT_PROVIDER)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
CccCryptoError::InternalError(format!(
|
||||||
|
"provider '{DEFAULT_PROVIDER}' not registered — call ccc_init() first"
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A lazily-initialised KemProvider instance.
|
||||||
|
///
|
||||||
|
/// `KemProvider` is not part of the `CryptoProvider` supertrait, so we keep
|
||||||
|
/// a standalone `WolfSslProvider` for KEM operations.
|
||||||
|
fn kem_provider() -> &'static WolfSslProvider {
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
static KEM: OnceLock<WolfSslProvider> = OnceLock::new();
|
||||||
|
KEM.get_or_init(WolfSslProvider::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Initialise the CCC cryptographic subsystem.
|
||||||
|
///
|
||||||
|
/// Registers the wolfSSL provider in the global registry.
|
||||||
|
/// Safe to call multiple times (idempotent).
|
||||||
|
pub fn ccc_init() {
|
||||||
|
dbg_bridge!("ccc_init: enter");
|
||||||
|
flutter_rust_bridge::setup_default_user_utils();
|
||||||
|
if !ProviderRegistry::global().contains(DEFAULT_PROVIDER) {
|
||||||
|
ccc_crypto_wolfssl::init();
|
||||||
|
}
|
||||||
|
dbg_bridge!("ccc_init: exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Provider info ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// List all registered provider names.
|
||||||
|
#[flutter_rust_bridge::frb(sync)]
|
||||||
|
pub fn ccc_list_providers() -> Vec<String> {
|
||||||
|
dbg_bridge!("ccc_list_providers");
|
||||||
|
ProviderRegistry::global().list()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the capabilities of the default provider.
|
||||||
|
pub fn ccc_capabilities() -> Result<CccCapabilities, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_capabilities");
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(CccCapabilities::from(provider.capabilities()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AEAD ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Encrypt with an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Returns ciphertext‖tag.
|
||||||
|
pub fn ccc_aead_encrypt(
|
||||||
|
algorithm: CccAeadAlgorithm,
|
||||||
|
key: Vec<u8>,
|
||||||
|
nonce: Vec<u8>,
|
||||||
|
plaintext: Vec<u8>,
|
||||||
|
aad: Vec<u8>,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_aead_encrypt: pt_len={}", plaintext.len());
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(provider.encrypt_aead(algorithm.to_core(), &key, &nonce, &plaintext, &aad)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt with an AEAD algorithm.
|
||||||
|
///
|
||||||
|
/// Expects ciphertext‖tag as `ciphertext`.
|
||||||
|
pub fn ccc_aead_decrypt(
|
||||||
|
algorithm: CccAeadAlgorithm,
|
||||||
|
key: Vec<u8>,
|
||||||
|
nonce: Vec<u8>,
|
||||||
|
ciphertext: Vec<u8>,
|
||||||
|
aad: Vec<u8>,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_aead_decrypt: ct_len={}", ciphertext.len());
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(provider.decrypt_aead(algorithm.to_core(), &key, &nonce, &ciphertext, &aad)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── KDF ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Derive key material using a KDF.
|
||||||
|
pub fn ccc_kdf_derive(
|
||||||
|
algorithm: CccKdfAlgorithm,
|
||||||
|
ikm: Vec<u8>,
|
||||||
|
salt: Vec<u8>,
|
||||||
|
info: Vec<u8>,
|
||||||
|
length: u32,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_kdf_derive: length={}", length);
|
||||||
|
let provider = default_provider()?;
|
||||||
|
let derived = provider.derive_key(
|
||||||
|
algorithm.to_core(),
|
||||||
|
&ikm,
|
||||||
|
&salt,
|
||||||
|
&info,
|
||||||
|
length as usize,
|
||||||
|
)?;
|
||||||
|
// Move out of Zeroizing wrapper — FRB will copy to Dart.
|
||||||
|
Ok(derived.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── MAC ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Compute a MAC tag.
|
||||||
|
pub fn ccc_mac_compute(
|
||||||
|
algorithm: CccMacAlgorithm,
|
||||||
|
key: Vec<u8>,
|
||||||
|
data: Vec<u8>,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_mac_compute: data_len={}", data.len());
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(provider.compute_mac(algorithm.to_core(), &key, &data)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify a MAC tag. Returns `true` if valid.
|
||||||
|
pub fn ccc_mac_verify(
|
||||||
|
algorithm: CccMacAlgorithm,
|
||||||
|
key: Vec<u8>,
|
||||||
|
data: Vec<u8>,
|
||||||
|
mac: Vec<u8>,
|
||||||
|
) -> Result<bool, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_mac_verify: data_len={}", data.len());
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(provider.verify_mac(algorithm.to_core(), &key, &data, &mac)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hash ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Compute a cryptographic hash.
|
||||||
|
pub fn ccc_hash(
|
||||||
|
algorithm: CccHashAlgorithm,
|
||||||
|
data: Vec<u8>,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_hash: data_len={}", data.len());
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(provider.hash(algorithm.to_core(), &data)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── KEM ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generate a KEM key pair.
|
||||||
|
pub fn ccc_kem_generate_keypair(
|
||||||
|
algorithm: CccKemAlgorithm,
|
||||||
|
) -> Result<CccKemKeyPair, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_kem_generate_keypair");
|
||||||
|
let kp = kem_provider().generate_keypair(algorithm.to_core())?;
|
||||||
|
Ok(CccKemKeyPair::from(kp))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM encapsulation — produce ciphertext + shared secret from a public key.
|
||||||
|
pub fn ccc_kem_encapsulate(
|
||||||
|
algorithm: CccKemAlgorithm,
|
||||||
|
public_key: Vec<u8>,
|
||||||
|
) -> Result<CccKemEncapResult, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_kem_encapsulate");
|
||||||
|
let result = kem_provider().encapsulate(algorithm.to_core(), &public_key)?;
|
||||||
|
Ok(CccKemEncapResult::from(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM decapsulation — recover shared secret from ciphertext + private key.
|
||||||
|
pub fn ccc_kem_decapsulate(
|
||||||
|
algorithm: CccKemAlgorithm,
|
||||||
|
private_key: Vec<u8>,
|
||||||
|
ciphertext: Vec<u8>,
|
||||||
|
) -> Result<Vec<u8>, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_kem_decapsulate");
|
||||||
|
let ss = kem_provider().decapsulate(algorithm.to_core(), &private_key, &ciphertext)?;
|
||||||
|
Ok(ss.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Self-test ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Run the provider self-test and return a structured report.
|
||||||
|
pub fn ccc_self_test() -> Result<CccSelfTestReport, CccCryptoError> {
|
||||||
|
dbg_bridge!("ccc_self_test");
|
||||||
|
let provider = default_provider()?;
|
||||||
|
Ok(CccSelfTestReport::from(provider.self_test()))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,328 @@
|
||||||
|
// Bridge-visible DTOs — no crypto logic, only type conversions.
|
||||||
|
//
|
||||||
|
// flutter_rust_bridge codegen picks up all pub types under crate::api
|
||||||
|
// and generates corresponding Dart classes.
|
||||||
|
|
||||||
|
use ccc_crypto_core::{
|
||||||
|
AeadAlgorithm, AlgoTestResult, AlgorithmCapability, HashAlgorithm, KdfAlgorithm,
|
||||||
|
KemAlgorithm, KemEncapResult, KemKeyPair, MacAlgorithm, ProviderCapabilities, SelfTestReport,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Algorithm ID enums (mirror Rust repr(u32) values) ────────────────────────
|
||||||
|
|
||||||
|
/// AEAD algorithm identifiers — values match `ccc_rust` `#[repr(u32)]`.
|
||||||
|
#[flutter_rust_bridge::frb(dart_metadata=("freezed"))]
|
||||||
|
pub enum CccAeadAlgorithm {
|
||||||
|
AesGcm256 = 12,
|
||||||
|
ChaCha20Poly1305 = 13,
|
||||||
|
XChaCha20Poly1305 = 14,
|
||||||
|
Ascon128a = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KDF algorithm identifiers.
|
||||||
|
#[flutter_rust_bridge::frb(dart_metadata=("freezed"))]
|
||||||
|
pub enum CccKdfAlgorithm {
|
||||||
|
Sha256 = 1,
|
||||||
|
Sha384 = 2,
|
||||||
|
Sha512 = 3,
|
||||||
|
Blake2b512 = 4,
|
||||||
|
Argon2id = 5,
|
||||||
|
Kmac256 = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MAC algorithm identifiers.
|
||||||
|
#[flutter_rust_bridge::frb(dart_metadata=("freezed"))]
|
||||||
|
pub enum CccMacAlgorithm {
|
||||||
|
HmacSha256 = 30,
|
||||||
|
HmacSha384 = 31,
|
||||||
|
HmacSha512 = 32,
|
||||||
|
Blake2bMac = 33,
|
||||||
|
Poly1305 = 35,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hash algorithm identifiers.
|
||||||
|
#[flutter_rust_bridge::frb(dart_metadata=("freezed"))]
|
||||||
|
pub enum CccHashAlgorithm {
|
||||||
|
Sha256 = 40,
|
||||||
|
Sha384 = 41,
|
||||||
|
Sha512 = 42,
|
||||||
|
Blake2b512 = 43,
|
||||||
|
Sha3_256 = 44,
|
||||||
|
Sha3_512 = 45,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM algorithm identifiers.
|
||||||
|
#[flutter_rust_bridge::frb(dart_metadata=("freezed"))]
|
||||||
|
pub enum CccKemAlgorithm {
|
||||||
|
X25519 = 50,
|
||||||
|
X448 = 51,
|
||||||
|
MlKem768 = 52,
|
||||||
|
MlKem1024 = 53,
|
||||||
|
ClassicMcEliece460896 = 54,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Enum conversions ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
impl CccAeadAlgorithm {
|
||||||
|
pub(crate) fn to_core(&self) -> AeadAlgorithm {
|
||||||
|
match self {
|
||||||
|
Self::AesGcm256 => AeadAlgorithm::AesGcm256,
|
||||||
|
Self::ChaCha20Poly1305 => AeadAlgorithm::ChaCha20Poly1305,
|
||||||
|
Self::XChaCha20Poly1305 => AeadAlgorithm::XChaCha20Poly1305,
|
||||||
|
Self::Ascon128a => AeadAlgorithm::Ascon128a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CccKdfAlgorithm {
|
||||||
|
pub(crate) fn to_core(&self) -> KdfAlgorithm {
|
||||||
|
match self {
|
||||||
|
Self::Sha256 => KdfAlgorithm::Sha256,
|
||||||
|
Self::Sha384 => KdfAlgorithm::Sha384,
|
||||||
|
Self::Sha512 => KdfAlgorithm::Sha512,
|
||||||
|
Self::Blake2b512 => KdfAlgorithm::Blake2b512,
|
||||||
|
Self::Argon2id => KdfAlgorithm::Argon2id,
|
||||||
|
Self::Kmac256 => KdfAlgorithm::Kmac256,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CccMacAlgorithm {
|
||||||
|
pub(crate) fn to_core(&self) -> MacAlgorithm {
|
||||||
|
match self {
|
||||||
|
Self::HmacSha256 => MacAlgorithm::HmacSha256,
|
||||||
|
Self::HmacSha384 => MacAlgorithm::HmacSha384,
|
||||||
|
Self::HmacSha512 => MacAlgorithm::HmacSha512,
|
||||||
|
Self::Blake2bMac => MacAlgorithm::Blake2bMac,
|
||||||
|
Self::Poly1305 => MacAlgorithm::Poly1305,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CccHashAlgorithm {
|
||||||
|
pub(crate) fn to_core(&self) -> HashAlgorithm {
|
||||||
|
match self {
|
||||||
|
Self::Sha256 => HashAlgorithm::Sha256,
|
||||||
|
Self::Sha384 => HashAlgorithm::Sha384,
|
||||||
|
Self::Sha512 => HashAlgorithm::Sha512,
|
||||||
|
Self::Blake2b512 => HashAlgorithm::Blake2b512,
|
||||||
|
Self::Sha3_256 => HashAlgorithm::Sha3_256,
|
||||||
|
Self::Sha3_512 => HashAlgorithm::Sha3_512,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CccKemAlgorithm {
|
||||||
|
pub(crate) fn to_core(&self) -> KemAlgorithm {
|
||||||
|
match self {
|
||||||
|
Self::X25519 => KemAlgorithm::X25519,
|
||||||
|
Self::X448 => KemAlgorithm::X448,
|
||||||
|
Self::MlKem768 => KemAlgorithm::MlKem768,
|
||||||
|
Self::MlKem1024 => KemAlgorithm::MlKem1024,
|
||||||
|
Self::ClassicMcEliece460896 => KemAlgorithm::ClassicMcEliece460896,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DTO structs ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Per-algorithm availability and quality scores.
|
||||||
|
pub struct CccAlgorithmCapability {
|
||||||
|
pub available: bool,
|
||||||
|
pub deterministic_io: bool,
|
||||||
|
pub efficiency_score: u8,
|
||||||
|
pub reliability_score: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&AlgorithmCapability> for CccAlgorithmCapability {
|
||||||
|
fn from(cap: &AlgorithmCapability) -> Self {
|
||||||
|
Self {
|
||||||
|
available: cap.available,
|
||||||
|
deterministic_io: cap.deterministic_io,
|
||||||
|
efficiency_score: cap.efficiency_score,
|
||||||
|
reliability_score: cap.reliability_score,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An algorithm entry in the capabilities list.
|
||||||
|
pub struct CccAlgorithmEntry {
|
||||||
|
pub algo_id: u32,
|
||||||
|
pub algo_name: String,
|
||||||
|
pub capability: CccAlgorithmCapability,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provider capabilities — flattened from HashMap to Vec for FRB.
|
||||||
|
pub struct CccCapabilities {
|
||||||
|
pub provider_name: String,
|
||||||
|
pub aead: Vec<CccAlgorithmEntry>,
|
||||||
|
pub kdf: Vec<CccAlgorithmEntry>,
|
||||||
|
pub mac: Vec<CccAlgorithmEntry>,
|
||||||
|
pub hash: Vec<CccAlgorithmEntry>,
|
||||||
|
pub kem: Vec<CccAlgorithmEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ProviderCapabilities> for CccCapabilities {
|
||||||
|
fn from(caps: ProviderCapabilities) -> Self {
|
||||||
|
Self {
|
||||||
|
provider_name: caps.provider_name,
|
||||||
|
aead: caps
|
||||||
|
.aead
|
||||||
|
.iter()
|
||||||
|
.map(|(algo, cap)| CccAlgorithmEntry {
|
||||||
|
algo_id: *algo as u32,
|
||||||
|
algo_name: algo.name().to_string(),
|
||||||
|
capability: CccAlgorithmCapability::from(cap),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
kdf: caps
|
||||||
|
.kdf
|
||||||
|
.iter()
|
||||||
|
.map(|(algo, cap)| CccAlgorithmEntry {
|
||||||
|
algo_id: *algo as u32,
|
||||||
|
algo_name: algo.name().to_string(),
|
||||||
|
capability: CccAlgorithmCapability::from(cap),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
mac: caps
|
||||||
|
.mac
|
||||||
|
.iter()
|
||||||
|
.map(|(algo, cap)| CccAlgorithmEntry {
|
||||||
|
algo_id: *algo as u32,
|
||||||
|
algo_name: algo.name().to_string(),
|
||||||
|
capability: CccAlgorithmCapability::from(cap),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
hash: caps
|
||||||
|
.hash
|
||||||
|
.iter()
|
||||||
|
.map(|(algo, cap)| CccAlgorithmEntry {
|
||||||
|
algo_id: *algo as u32,
|
||||||
|
algo_name: algo.name().to_string(),
|
||||||
|
capability: CccAlgorithmCapability::from(cap),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
kem: caps
|
||||||
|
.kem
|
||||||
|
.iter()
|
||||||
|
.map(|(algo, cap)| CccAlgorithmEntry {
|
||||||
|
algo_id: *algo as u32,
|
||||||
|
algo_name: algo.name().to_string(),
|
||||||
|
capability: CccAlgorithmCapability::from(cap),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM key pair — public + private key bytes.
|
||||||
|
pub struct CccKemKeyPair {
|
||||||
|
pub public_key: Vec<u8>,
|
||||||
|
pub private_key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KemKeyPair> for CccKemKeyPair {
|
||||||
|
fn from(kp: KemKeyPair) -> Self {
|
||||||
|
Self {
|
||||||
|
public_key: kp.public_key.clone(),
|
||||||
|
private_key: kp.private_key.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KEM encapsulation result — ciphertext + shared secret.
|
||||||
|
pub struct CccKemEncapResult {
|
||||||
|
pub ciphertext: Vec<u8>,
|
||||||
|
pub shared_secret: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KemEncapResult> for CccKemEncapResult {
|
||||||
|
fn from(er: KemEncapResult) -> Self {
|
||||||
|
Self {
|
||||||
|
ciphertext: er.ciphertext.clone(),
|
||||||
|
shared_secret: er.shared_secret.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per-algorithm self-test result.
|
||||||
|
pub struct CccAlgoTestResult {
|
||||||
|
pub algo_id: u32,
|
||||||
|
pub algo_name: String,
|
||||||
|
pub passed: bool,
|
||||||
|
pub error_message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&AlgoTestResult> for CccAlgoTestResult {
|
||||||
|
fn from(r: &AlgoTestResult) -> Self {
|
||||||
|
Self {
|
||||||
|
algo_id: r.algo_id,
|
||||||
|
algo_name: r.algo_name.clone(),
|
||||||
|
passed: r.passed,
|
||||||
|
error_message: r.error_message.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aggregate self-test report.
|
||||||
|
pub struct CccSelfTestReport {
|
||||||
|
pub provider_name: String,
|
||||||
|
pub results: Vec<CccAlgoTestResult>,
|
||||||
|
pub all_passed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SelfTestReport> for CccSelfTestReport {
|
||||||
|
fn from(report: SelfTestReport) -> Self {
|
||||||
|
Self {
|
||||||
|
provider_name: report.provider_name,
|
||||||
|
results: report.results.iter().map(CccAlgoTestResult::from).collect(),
|
||||||
|
all_passed: report.all_passed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bridge error ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Bridge-visible error type — maps 1:1 from `ccc_rust::CryptoError`.
|
||||||
|
///
|
||||||
|
/// FRB translates this into a Dart exception.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CccCryptoError {
|
||||||
|
UnsupportedAlgorithm(String),
|
||||||
|
InvalidKey(String),
|
||||||
|
InvalidNonce(String),
|
||||||
|
AuthenticationFailed,
|
||||||
|
InvalidInput(String),
|
||||||
|
FeatureNotCompiled(String),
|
||||||
|
InternalError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CccCryptoError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UnsupportedAlgorithm(s) => write!(f, "unsupported algorithm: {s}"),
|
||||||
|
Self::InvalidKey(s) => write!(f, "invalid key: {s}"),
|
||||||
|
Self::InvalidNonce(s) => write!(f, "invalid nonce: {s}"),
|
||||||
|
Self::AuthenticationFailed => write!(f, "authentication failed"),
|
||||||
|
Self::InvalidInput(s) => write!(f, "invalid input: {s}"),
|
||||||
|
Self::FeatureNotCompiled(s) => write!(f, "feature not compiled: {s}"),
|
||||||
|
Self::InternalError(s) => write!(f, "internal error: {s}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for CccCryptoError {}
|
||||||
|
|
||||||
|
impl From<ccc_crypto_core::CryptoError> for CccCryptoError {
|
||||||
|
fn from(e: ccc_crypto_core::CryptoError) -> Self {
|
||||||
|
match e {
|
||||||
|
ccc_crypto_core::CryptoError::UnsupportedAlgorithm(s) => Self::UnsupportedAlgorithm(s),
|
||||||
|
ccc_crypto_core::CryptoError::InvalidKey(s) => Self::InvalidKey(s),
|
||||||
|
ccc_crypto_core::CryptoError::InvalidNonce(s) => Self::InvalidNonce(s),
|
||||||
|
ccc_crypto_core::CryptoError::AuthenticationFailed => Self::AuthenticationFailed,
|
||||||
|
ccc_crypto_core::CryptoError::InvalidInput(s) => Self::InvalidInput(s),
|
||||||
|
ccc_crypto_core::CryptoError::FeatureNotCompiled(s) => Self::FeatureNotCompiled(s),
|
||||||
|
ccc_crypto_core::CryptoError::InternalError(s) => Self::InternalError(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod crypto;
|
||||||
|
pub mod dto;
|
||||||
|
pub mod simple;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
|
||||||
|
pub fn greet(name: String) -> String {
|
||||||
|
format!("Hello, {name}!")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[flutter_rust_bridge::frb(init)]
|
||||||
|
pub fn init_app() {
|
||||||
|
// Default utilities - feel free to customize
|
||||||
|
flutter_rust_bridge::setup_default_user_utils();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod api;
|
||||||
|
mod frb_generated;
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# The Flutter tooling requires that developers have CMake 3.10 or later
|
|
||||||
# installed. You should not increase this version, as doing so will cause
|
|
||||||
# the plugin to fail to compile for some customers of the plugin.
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
|
|
||||||
project(ccc_cryptography_library VERSION 0.0.1 LANGUAGES C)
|
|
||||||
|
|
||||||
add_library(ccc_cryptography SHARED
|
|
||||||
"ccc_cryptography.c"
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(ccc_cryptography PROPERTIES
|
|
||||||
PUBLIC_HEADER ccc_cryptography.h
|
|
||||||
OUTPUT_NAME "ccc_cryptography"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(ccc_cryptography PUBLIC DART_SHARED_LIB)
|
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
# Support Android 15 16k page size
|
|
||||||
target_link_options(ccc_cryptography PRIVATE "-Wl,-z,max-page-size=16384")
|
|
||||||
endif()
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#include "ccc_cryptography.h"
|
|
||||||
|
|
||||||
// A very short-lived native function.
|
|
||||||
//
|
|
||||||
// For very short-lived functions, it is fine to call them on the main isolate.
|
|
||||||
// They will block the Dart execution while running the native function, so
|
|
||||||
// only do this for native functions which are guaranteed to be short-lived.
|
|
||||||
FFI_PLUGIN_EXPORT int sum(int a, int b) { return a + b; }
|
|
||||||
|
|
||||||
// A longer-lived native function, which occupies the thread calling it.
|
|
||||||
//
|
|
||||||
// Do not call these kind of native functions in the main isolate. They will
|
|
||||||
// block Dart execution. This will cause dropped frames in Flutter applications.
|
|
||||||
// Instead, call these native functions on a separate isolate.
|
|
||||||
FFI_PLUGIN_EXPORT int sum_long_running(int a, int b) {
|
|
||||||
// Simulate work.
|
|
||||||
#if _WIN32
|
|
||||||
Sleep(5000);
|
|
||||||
#else
|
|
||||||
usleep(5000 * 1000);
|
|
||||||
#endif
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
#define FFI_PLUGIN_EXPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A very short-lived native function.
|
|
||||||
//
|
|
||||||
// For very short-lived functions, it is fine to call them on the main isolate.
|
|
||||||
// They will block the Dart execution while running the native function, so
|
|
||||||
// only do this for native functions which are guaranteed to be short-lived.
|
|
||||||
FFI_PLUGIN_EXPORT int sum(int a, int b);
|
|
||||||
|
|
||||||
// A longer lived native function, which occupies the thread calling it.
|
|
||||||
//
|
|
||||||
// Do not call these kind of native functions in the main isolate. They will
|
|
||||||
// block Dart execution. This will cause dropped frames in Flutter applications.
|
|
||||||
// Instead, call these native functions on a separate isolate.
|
|
||||||
FFI_PLUGIN_EXPORT int sum_long_running(int a, int b);
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import 'package:integration_test/integration_test_driver.dart';
|
||||||
|
|
||||||
|
Future<void> main() => integrationDriver();
|
||||||
|
|
@ -8,16 +8,13 @@ cmake_minimum_required(VERSION 3.14)
|
||||||
set(PROJECT_NAME "ccc_cryptography")
|
set(PROJECT_NAME "ccc_cryptography")
|
||||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||||
|
|
||||||
# Invoke the build for native code shared with the other target platforms.
|
include("../cargokit/cmake/cargokit.cmake")
|
||||||
# This can be changed to accommodate different builds.
|
apply_cargokit(${PROJECT_NAME} ../rust ccc_cryptography "")
|
||||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
|
|
||||||
|
|
||||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||||
# This list could contain prebuilt libraries, or libraries created by an
|
# This list could contain prebuilt libraries, or libraries created by an
|
||||||
# external build triggered from this build file.
|
# external build triggered from this build file.
|
||||||
set(ccc_cryptography_bundled_libraries
|
set(ccc_cryptography_bundled_libraries
|
||||||
# Defined in ../src/CMakeLists.txt.
|
"${${PROJECT_NAME}_cargokit_lib}"
|
||||||
# This can be changed to accommodate different builds.
|
|
||||||
$<TARGET_FILE:ccc_cryptography>
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue