From b52ca03effc225fb91085167db148b1c34adc165 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Fri, 8 Sep 2023 19:25:26 +0200 Subject: [PATCH] feat: Bring over the cryptography API from moxplatform --- android/src/main/AndroidManifest.xml | 2 +- .../org/moxxy/moxxy_native/Constants.kt | 3 + .../moxxy/moxxy_native/MoxxyNativePlugin.kt | 4 + .../cryptography/CryptographyApi.kt | 192 ++++++++++++++++++ .../CryptographyImplementation.kt | 169 +++++++++++++++ .../cryptography/HashedFileOutputStream.kt | 25 +++ example/lib/main.dart | 53 ++++- example/pigeon/cryptography.dart | 0 example/pubspec.lock | 50 +---- lib/moxxy_native.dart | 1 + lib/pigeon/cryptography.g.dart | 141 +++++++++++++ pigeon/cryptography.dart | 34 ++++ 12 files changed, 623 insertions(+), 51 deletions(-) create mode 100644 android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt create mode 100644 android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyImplementation.kt create mode 100644 android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/HashedFileOutputStream.kt create mode 100644 example/pigeon/cryptography.dart create mode 100644 lib/pigeon/cryptography.g.dart create mode 100644 pigeon/cryptography.dart diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 9de0eaa..146511a 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ private lateinit var pickerListener: PickerResultListener + private val cryptographyImplementation = CryptographyImplementation() override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { context = flutterPluginBinding.applicationContext MoxxyPickerApi.setUp(flutterPluginBinding.binaryMessenger, this) MoxxyNotificationsApi.setUp(flutterPluginBinding.binaryMessenger, this) + MoxxyCryptographyApi.setUp(flutterPluginBinding.binaryMessenger, cryptographyImplementation) pickerListener = PickerResultListener(context!!) Log.d(TAG, "Attached to engine") } diff --git a/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt new file mode 100644 index 0000000..b1f35cb --- /dev/null +++ b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt @@ -0,0 +1,192 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package org.moxxy.moxxy_native.cryptography + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + if (exception is FlutterError) { + return listOf( + exception.code, + exception.message, + exception.details, + ) + } else { + return listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception), + ) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null, +) : Throwable() + +enum class CipherAlgorithm(val raw: Int) { + AES128GCMNOPADDING(0), + AES256GCMNOPADDING(1), + AES256CBCPKCS7(2), + ; + + companion object { + fun ofRaw(raw: Int): CipherAlgorithm? { + return values().firstOrNull { it.raw == raw } + } + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class CryptographyResult( + val plaintextHash: ByteArray, + val ciphertextHash: ByteArray, + +) { + companion object { + @Suppress("UNCHECKED_CAST") + fun fromList(list: List): CryptographyResult { + val plaintextHash = list[0] as ByteArray + val ciphertextHash = list[1] as ByteArray + return CryptographyResult(plaintextHash, ciphertextHash) + } + } + fun toList(): List { + return listOf( + plaintextHash, + ciphertextHash, + ) + } +} + +@Suppress("UNCHECKED_CAST") +private object MoxxyCryptographyApiCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 128.toByte() -> { + return (readValue(buffer) as? List)?.let { + CryptographyResult.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is CryptographyResult -> { + stream.write(128) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface MoxxyCryptographyApi { + fun encryptFile(sourcePath: String, destPath: String, key: ByteArray, iv: ByteArray, algorithm: CipherAlgorithm, hashSpec: String, callback: (Result) -> Unit) + fun decryptFile(sourcePath: String, destPath: String, key: ByteArray, iv: ByteArray, algorithm: CipherAlgorithm, hashSpec: String, callback: (Result) -> Unit) + fun hashFile(sourcePath: String, hashSpec: String, callback: (Result) -> Unit) + + companion object { + /** The codec used by MoxxyCryptographyApi. */ + val codec: MessageCodec by lazy { + MoxxyCryptographyApiCodec + } + + /** Sets up an instance of `MoxxyCryptographyApi` to handle messages through the `binaryMessenger`. */ + @Suppress("UNCHECKED_CAST") + fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyCryptographyApi?) { + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.encryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val sourcePathArg = args[0] as String + val destPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val ivArg = args[3] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!! + val hashSpecArg = args[5] as String + api.encryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.decryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val sourcePathArg = args[0] as String + val destPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val ivArg = args[3] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!! + val hashSpecArg = args[5] as String + api.decryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.hashFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val sourcePathArg = args[0] as String + val hashSpecArg = args[1] as String + api.hashFile(sourcePathArg, hashSpecArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyImplementation.kt b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyImplementation.kt new file mode 100644 index 0000000..84be49d --- /dev/null +++ b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyImplementation.kt @@ -0,0 +1,169 @@ +package org.moxxy.moxxy_native.cryptography + +import android.util.Log +import org.moxxy.moxxy_native.BUFFER_SIZE +import org.moxxy.moxxy_native.TAG +import java.io.FileInputStream +import java.security.MessageDigest +import javax.crypto.Cipher +import javax.crypto.CipherOutputStream +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import kotlin.concurrent.thread + +/* + * Convert the algorithm spec @algorithm to the format that Java/Android understands + * */ +private fun getCipherSpecFromInteger(algorithm: CipherAlgorithm): String { + return when (algorithm) { + CipherAlgorithm.AES128GCMNOPADDING -> "AES_128/GCM/NoPadding" + CipherAlgorithm.AES256GCMNOPADDING -> "AES_256/GCM/NoPadding" + CipherAlgorithm.AES256CBCPKCS7 -> "AES_256/CBC/PKCS7PADDING" + } +} + +/* + * Implementation of Moxxy's cryptography API + * */ +class CryptographyImplementation : MoxxyCryptographyApi { + override fun encryptFile( + sourcePath: String, + destPath: String, + key: ByteArray, + iv: ByteArray, + algorithm: CipherAlgorithm, + hashSpec: String, + callback: (Result) -> Unit, + ) { + thread(start = true) { + val cipherSpec = getCipherSpecFromInteger(algorithm) + val buffer = ByteArray(BUFFER_SIZE) + val secretKey = SecretKeySpec(key, cipherSpec) + val inputStream = FileInputStream(sourcePath) + try { + val digest = MessageDigest.getInstance(hashSpec) + val cipher = Cipher.getInstance(cipherSpec).apply { + init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv)) + } + val fileOutputStream = HashedFileOutputStream(destPath, hashSpec) + val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher) + var length: Int + while (true) { + length = inputStream.read(buffer) + if (length <= 0) break + + digest.update(buffer, 0, length) + cipherOutputStream.write(buffer, 0, length) + } + + // Clean up + cipherOutputStream.apply { + flush() + close() + } + + // Success + callback( + Result.success( + CryptographyResult( + plaintextHash = digest.digest(), + ciphertextHash = fileOutputStream.digest(), + ), + ), + ) + } catch (ex: Exception) { + Log.e(TAG, "Failed to encrypt file $sourcePath: ${ex.message}") + callback(Result.success(null)) + } finally { + // Clean up + inputStream.close() + } + } + } + + override fun decryptFile( + sourcePath: String, + destPath: String, + key: ByteArray, + iv: ByteArray, + algorithm: CipherAlgorithm, + hashSpec: String, + callback: (Result) -> Unit, + ) { + thread(start = true) { + val cipherSpec = getCipherSpecFromInteger(algorithm) + val buffer = ByteArray(BUFFER_SIZE) + val secretKey = SecretKeySpec(key, cipherSpec) + val inputStream = FileInputStream(sourcePath) + try { + val digest = MessageDigest.getInstance(hashSpec) + val cipher = Cipher.getInstance(cipherSpec).apply { + init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv)) + } + val fileOutputStream = HashedFileOutputStream(destPath, hashSpec) + val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher) + var length: Int + while (true) { + length = inputStream.read(buffer) + if (length <= 0) break + + digest.update(buffer, 0, length) + cipherOutputStream.write(buffer, 0, length) + } + + // Clean up + cipherOutputStream.apply { + flush() + close() + } + + // Success + callback( + Result.success( + CryptographyResult( + plaintextHash = digest.digest(), + ciphertextHash = fileOutputStream.digest(), + ), + ), + ) + } catch (ex: Exception) { + Log.e(TAG, "Failed to decrypt file $sourcePath: ${ex.message}") + callback(Result.success(null)) + } finally { + // Clean up + inputStream.close() + } + } + } + + override fun hashFile( + sourcePath: String, + hashSpec: String, + callback: (Result) -> Unit, + ) { + thread(start = true) { + val buffer = ByteArray(BUFFER_SIZE) + val inputStream = FileInputStream(sourcePath) + try { + val digest = MessageDigest.getInstance(hashSpec) + var length: Int + while (true) { + length = inputStream.read(buffer) + if (length <= 0) break + + // Only update the digest if we read more than 0 bytes + digest.update(buffer, 0, length) + } + + // Return success + callback(Result.success(digest.digest())) + } catch (ex: Exception) { + Log.e(TAG, "Failed to has file $sourcePath with $hashSpec: ${ex.message}") + callback(Result.success(null)) + } finally { + // Clean up + inputStream.close() + } + } + } +} diff --git a/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/HashedFileOutputStream.kt b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/HashedFileOutputStream.kt new file mode 100644 index 0000000..aa00bdd --- /dev/null +++ b/android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/HashedFileOutputStream.kt @@ -0,0 +1,25 @@ +package org.moxxy.moxxy_native.cryptography + +import java.io.FileOutputStream +import java.security.MessageDigest + +/* + * A FileOutputStream that continuously hashes whatever it writes to the file. + */ +class HashedFileOutputStream(name: String, hashAlgorithm: String) : FileOutputStream(name) { + private val digest: MessageDigest + + init { + this.digest = MessageDigest.getInstance(hashAlgorithm) + } + + override fun write(buffer: ByteArray, offset: Int, length: Int) { + super.write(buffer, offset, length) + + digest.update(buffer, offset, length) + } + + fun digest(): ByteArray { + return digest.digest() + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 715e251..5445d15 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:moxxy_native/moxxy_native.dart'; @@ -5,9 +7,16 @@ void main() { runApp(const MyApp()); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State { + String? imagePath; + @override Widget build(BuildContext context) { return MaterialApp( @@ -44,6 +53,48 @@ class MyApp extends StatelessWidget { }, child: const Text('Generic multi-picker'), ), + TextButton( + onPressed: () async { + final result = await MoxxyPickerApi().pickFiles(FilePickerType.image, false); + if (result.isEmpty) return; + + final encDest = result.first! + '.enc'; + final decDest = result.first! + '.dec'; + final encResult = await MoxxyCryptographyApi().encryptFile( + result.first!, + encDest, + Uint8List.fromList(List.filled(32, 1)), + Uint8List.fromList(List.filled(16, 2)), + CipherAlgorithm.aes256CbcPkcs7, + 'SHA-256', + ); + if (encResult == null) { + print('Failed to encrypt file'); + return; + } + + final decResult = await MoxxyCryptographyApi().decryptFile( + encDest, + decDest, + Uint8List.fromList(List.filled(32, 1)), + Uint8List.fromList(List.filled(16, 2)), + CipherAlgorithm.aes256CbcPkcs7, + 'SHA-256', + ); + if (decResult == null) { + print('Failed to decrypt file'); + return; + } + + setState(() { + imagePath = decDest; + }); + }, + child: Text('Test cryptography'), + ), + + if (imagePath != null) + Image.file(File(imagePath!)), ], ), ), diff --git a/example/pigeon/cryptography.dart b/example/pigeon/cryptography.dart new file mode 100644 index 0000000..e69de29 diff --git a/example/pubspec.lock b/example/pubspec.lock index 9d7235d..14fd041 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -130,54 +130,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 - url: "https://pub.dev" - source: hosted - version: "10.4.5" - permission_handler_android: - dependency: transitive - description: - name: permission_handler_android - sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" - url: "https://pub.dev" - source: hosted - version: "10.3.6" - permission_handler_apple: - dependency: transitive - description: - name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" - url: "https://pub.dev" - source: hosted - version: "9.1.4" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2 - url: "https://pub.dev" - source: hosted - version: "3.11.5" - permission_handler_windows: - dependency: transitive - description: - name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 - url: "https://pub.dev" - source: hosted - version: "0.1.3" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d - url: "https://pub.dev" - source: hosted - version: "2.1.6" sky_engine: dependency: transitive description: flutter @@ -241,4 +193,4 @@ packages: version: "2.1.4" sdks: dart: ">=2.19.6 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.5.0" diff --git a/lib/moxxy_native.dart b/lib/moxxy_native.dart index f49b8b5..929e431 100644 --- a/lib/moxxy_native.dart +++ b/lib/moxxy_native.dart @@ -1,2 +1,3 @@ +export 'pigeon/cryptography.g.dart'; export 'pigeon/notifications.g.dart'; export 'pigeon/picker.g.dart'; diff --git a/lib/pigeon/cryptography.g.dart b/lib/pigeon/cryptography.g.dart new file mode 100644 index 0000000..0ec046f --- /dev/null +++ b/lib/pigeon/cryptography.g.dart @@ -0,0 +1,141 @@ +// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum CipherAlgorithm { + aes128GcmNoPadding, + aes256GcmNoPadding, + aes256CbcPkcs7, +} + +class CryptographyResult { + CryptographyResult({ + required this.plaintextHash, + required this.ciphertextHash, + }); + + Uint8List plaintextHash; + + Uint8List ciphertextHash; + + Object encode() { + return [ + plaintextHash, + ciphertextHash, + ]; + } + + static CryptographyResult decode(Object result) { + result as List; + return CryptographyResult( + plaintextHash: result[0]! as Uint8List, + ciphertextHash: result[1]! as Uint8List, + ); + } +} + +class _MoxxyCryptographyApiCodec extends StandardMessageCodec { + const _MoxxyCryptographyApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CryptographyResult) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CryptographyResult.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class MoxxyCryptographyApi { + /// Constructor for [MoxxyCryptographyApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + MoxxyCryptographyApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _MoxxyCryptographyApiCodec(); + + Future encryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.encryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as CryptographyResult?); + } + } + + Future decryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.decryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as CryptographyResult?); + } + } + + Future hashFile(String arg_sourcePath, String arg_hashSpec) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.hashFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_sourcePath, arg_hashSpec]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } +} diff --git a/pigeon/cryptography.dart b/pigeon/cryptography.dart new file mode 100644 index 0000000..9f786e2 --- /dev/null +++ b/pigeon/cryptography.dart @@ -0,0 +1,34 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/pigeon/cryptography.g.dart', + kotlinOut: 'android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt', + kotlinOptions: KotlinOptions( + package: 'org.moxxy.moxxy_native.cryptography', + ), + ), +) +enum CipherAlgorithm { + aes128GcmNoPadding, + aes256GcmNoPadding, + aes256CbcPkcs7; +} + +class CryptographyResult { + const CryptographyResult(this.plaintextHash, this.ciphertextHash); + final Uint8List plaintextHash; + final Uint8List ciphertextHash; +} + +@HostApi() +abstract class MoxxyCryptographyApi { + @async + CryptographyResult? encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec); + + @async + CryptographyResult? decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec); + + @async + Uint8List? hashFile(String sourcePath, String hashSpec); +}