diff --git a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart index 957619e..dd61735 100644 --- a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart +++ b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; @@ -30,6 +32,29 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource { return plainText; } + @override + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + final plainText = File.fromUri( + Uri.parse( + '${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}', + ), + ); + await cipher.decryptFile( + cipherText, + plainText, + ); + } + @override Future deriveKeyFromPassword( String password, { @@ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource { return cipherText.bytes; } + @override + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + final cipherText = File.fromUri( + Uri.parse( + '${folderResult.path}/${plainText.path.split('/').last}.enc', + ), + ); + + await cipher.encryptFile(plainText, cipherText); + } + @override Future generateSecureRandom(int length) async { final SecretKey sk = await SecretKey.fromSecureRandom(length); diff --git a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart index d4921eb..e14d14f 100644 --- a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart +++ b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart @@ -6,6 +6,8 @@ // ignore_for_file: implementation_imports +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; @@ -37,6 +39,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { return paddedPlainText; } + @override + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) async { + throw UnimplementedError(); + } + @override Future deriveKeyFromPassword( String password, { @@ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { @override Future encryptWithIV( - Uint8List data, SecretKey key, Uint8List iv,) async { + Uint8List data, + SecretKey key, + Uint8List iv, + ) async { final gcm = GCMBlockCipher(AESEngine()) ..init( true, @@ -144,6 +158,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { ); } + @override + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) async { + throw UnimplementedError(); + } + @override Future generateSecureRandom(int length) async { if (_secureRandom == null) { diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart index 818aa21..2781fb3 100644 --- a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart +++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; import 'dart:typed_data'; import 'package:native_crypto/native_crypto.dart'; @@ -30,6 +31,22 @@ class CryptoRepositoryImpl extends CryptoRepository { }, ); + @override + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.decryptFile(cipherText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + @override FutureOrResult deriveKeyFromPassword( String password, { @@ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository { @override FutureOrResult encryptWithIV( - Uint8List data, SecretKey key, Uint8List iv,) => + Uint8List data, + SecretKey key, + Uint8List iv, + ) => Result.tryCatchAsync( () async => cryptoDataSource.encryptWithIV(data, key, iv), (error) { @@ -97,6 +117,22 @@ class CryptoRepositoryImpl extends CryptoRepository { }, ); + @override + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptFile(plainText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + @override FutureOrResult generateSecureRandom(int length) => Result.tryCatchAsync( diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart new file mode 100644 index 0000000..972ac7b --- /dev/null +++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart @@ -0,0 +1,167 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class CryptoRepositorySwitchableImpl extends CryptoRepository { + CryptoRepositorySwitchableImpl({ + required this.nativeCryptoDataSource, + required this.pointyCastleDataSource, + required this.currentMode, + }); + + CryptoDataSource nativeCryptoDataSource; + CryptoDataSource pointyCastleDataSource; + Mode currentMode; + + set mode(Mode mode) { + currentMode = mode; + } + + CryptoDataSource get cryptoDataSource { + if (currentMode is NativeCryptoMode) { + return nativeCryptoDataSource; + } else if (currentMode is PointyCastleMode) { + return pointyCastleDataSource; + } else { + throw Exception('Unknown mode'); + } + } + + @override + FutureOrResult decrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.decrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.decryptFile(cipherText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult deriveKeyFromPassword( + String password, { + required String salt, + }) => + Result.tryCatchAsync( + () async => cryptoDataSource.deriveKeyFromPassword( + password, + salt: salt, + ), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hash(Hash hasher, Uint8List data) => + Result.tryCatchAsync( + () async => cryptoDataSource.hash(hasher, data), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.hmac(hmac, data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.encrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encryptWithIV( + Uint8List data, + SecretKey key, + Uint8List iv, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptWithIV(data, key, iv), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptFile(plainText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult generateSecureRandom(int length) => + Result.tryCatchAsync( + () async => cryptoDataSource.generateSecureRandom(length), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); +} diff --git a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart index 69db521..df7f304 100644 --- a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart +++ b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; @@ -15,12 +17,22 @@ abstract class CryptoDataSource extends BaseDataSource { required String salt, }); Future encrypt(Uint8List data, SecretKey key); + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ); Future encryptWithIV( Uint8List data, SecretKey key, Uint8List iv, ); Future decrypt(Uint8List data, SecretKey key); + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ); Future hash(Hash hasher, Uint8List data); Future hmac(Hmac hmac, Uint8List data, SecretKey key); } diff --git a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart index 6bf54ea..6045be6 100644 --- a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart +++ b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; @@ -15,12 +17,22 @@ abstract class CryptoRepository extends BaseRepository { required String salt, }); FutureOrResult encrypt(Uint8List data, SecretKey key); + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ); FutureOrResult encryptWithIV( Uint8List data, SecretKey key, Uint8List iv, ); FutureOrResult decrypt(Uint8List data, SecretKey key); + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ); FutureOrResult hash(Hash hasher, Uint8List data); FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key); diff --git a/packages/native_crypto/example/lib/presentation/app/app.dart b/packages/native_crypto/example/lib/presentation/app/app.dart index 0769515..f9253a4 100644 --- a/packages/native_crypto/example/lib/presentation/app/app.dart +++ b/packages/native_crypto/example/lib/presentation/app/app.dart @@ -8,9 +8,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:native_crypto_example/core/get_it.dart'; import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart'; -import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart'; +import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart'; +import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart'; import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart'; import 'package:native_crypto_example/data/repositories/session_repository_impl.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; import 'package:native_crypto_example/domain/repositories/session_repository.dart'; @@ -28,6 +30,12 @@ class App extends StatelessWidget { final SessionRepository _sessionRepository = SessionRepositoryImpl(sessionDataSource: getIt()); + final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl( + nativeCryptoDataSource: getIt(), + pointyCastleDataSource: getIt(), + currentMode: const NativeCryptoMode(), + ); + @override Widget build(BuildContext context) => MultiProvider( repositoryProviders: [ @@ -35,18 +43,17 @@ class App extends StatelessWidget { RepositoryProvider.value( value: _sessionRepository, ), - RepositoryProvider( - create: (_) => CryptoRepositoryImpl( - cryptoDataSource: getIt(), - ), - ), + RepositoryProvider.value(value: _cryptoRepository), ], blocProviders: [ BlocProvider( create: (_) => OutputCubit(_loggerRepository), ), BlocProvider( - create: (_) => ModeSwitcherCubit(_sessionRepository), + create: (_) => ModeSwitcherCubit( + _sessionRepository, + _cryptoRepository, + ), ) ], child: MaterialApp( diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart new file mode 100644 index 0000000..3c766fe --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart @@ -0,0 +1,132 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; + +part 'benchmark_state.dart'; + +class BenchmarkCubit extends Cubit { + BenchmarkCubit({ + required this.sessionRepository, + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const BenchmarkState.initial()); + final SessionRepository sessionRepository; + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + List testedSizes = [ + 2097152, + 6291456, + 10485760, + 14680064, + 18874368, + 23068672, + 27262976, + 31457280, + 35651584, + 39845888, + 44040192, + 48234496, + 52428800, + ]; + + FutureOr launchBenchmark() async { + emit(const BenchmarkState.loading()); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + BenchmarkState.failure( + sk.err?.message, + ), + ); + } + + int run = 0; + final csv = StringBuffer( + 'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n', + ); + for (final size in testedSizes) { + run++; + final StringBuffer csvLine = StringBuffer(); + final dummyBytes = Uint8List(size); + csvLine.write('$run;$size;'); + + // Encryption + final beforeEncryption = DateTime.now(); + + final encryptedBigFileResult = await cryptoRepository.encrypt( + dummyBytes, + sk.ok!, + ); + + final afterEncryption = DateTime.now(); + + final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch - + beforeEncryption.millisecondsSinceEpoch; + + await loggerRepository.addLog( + LogInfo( + '[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms', + ), + ); + + csvLine.write('$benchmarkEncryption'); + + if (encryptedBigFileResult.isErr) { + await loggerRepository.addLog( + LogError( + 'Encryption failed: ${encryptedBigFileResult.err?.message}', + ), + ); + emit( + BenchmarkState.failure( + encryptedBigFileResult.err?.message, + ), + ); + return; + } + + // Decryption + final beforeDecryption = DateTime.now(); + await cryptoRepository.decrypt( + encryptedBigFileResult.ok!, + sk.ok!, + ); + final afterDecryption = DateTime.now(); + final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch - + beforeDecryption.millisecondsSinceEpoch; + await loggerRepository.addLog( + LogInfo( + '[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms', + ), + ); + + csvLine.write(';$benchmarkDecryption'); + csv.writeln(csvLine); + } + debugPrint(csv.toString()); + emit( + const BenchmarkState.success(), + ); + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart new file mode 100644 index 0000000..8cf7978 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'benchmark_cubit.dart'; + +@immutable +class BenchmarkState { + const BenchmarkState.initial() + : state = State.initial, + error = null; + + const BenchmarkState.loading() + : state = State.loading, + error = null; + + const BenchmarkState.failure(this.error) : state = State.failure; + + const BenchmarkState.success() + : state = State.success, + error = null; + + final State state; + final String? error; +} diff --git a/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart new file mode 100644 index 0000000..dc2627e --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart @@ -0,0 +1,55 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class BenchmarkStateManagement + extends CubitScreen { + const BenchmarkStateManagement({super.key}); + + @override + BenchmarkCubit create(BuildContext context) => BenchmarkCubit( + sessionRepository: repo(context), + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, BenchmarkState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Benchmark', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Launch', + onPressed: () => bloc(context).launchBenchmark(), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart index bb004cd..13b3435 100644 --- a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart +++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart @@ -5,7 +5,9 @@ // https://opensource.org/licenses/MIT. import 'dart:async'; +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:native_crypto/native_crypto.dart'; @@ -232,4 +234,184 @@ class AESCubit extends Cubit { return; } + + FutureOr encryptFile() async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + error: sk.err?.message, + ), + ); + + return; + } + + // Pick file to encrypt + final pickFileResult = await FilePicker.platform.pickFiles(); + + if (pickFileResult == null) { + await loggerRepository.addLog( + const LogError('No file selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No file selected.', + ), + ); + + return; + } + + final file = File(pickFileResult.files.single.path!); + + // Pick folder to store the encrypted file + final resultFolder = await FilePicker.platform.getDirectoryPath(); + + if (resultFolder == null) { + await loggerRepository.addLog( + const LogError('No folder selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No folder selected.', + ), + ); + + return; + } + + final folder = Directory(resultFolder); + + final encryption = await cryptoRepository.encryptFile( + file, + folder.uri, + sk.ok!, + ); + + emit( + await encryption.foldAsync( + (_) async { + await loggerRepository.addLog( + const LogInfo('File successfully encrypted.\n'), + ); + return state.copyWith( + state: State.success, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during encryption.'), + ); + return state.copyWith( + state: State.failure, + error: error.message, + ); + }, + ), + ); + + return; + } + + FutureOr decryptFile() async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + error: sk.err?.message, + ), + ); + + return; + } + + await FilePicker.platform.clearTemporaryFiles(); + + final resultPickFile = await FilePicker.platform.pickFiles(); + + if (resultPickFile == null) { + await loggerRepository.addLog( + const LogError('No file selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No file selected.', + ), + ); + + return; + } + + final file = File(resultPickFile.files.single.path!); + + // Pick folder to store the encrypted file + final resultFolder = await FilePicker.platform.getDirectoryPath(); + + if (resultFolder == null) { + await loggerRepository.addLog( + const LogError('No folder selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No folder selected.', + ), + ); + + return; + } + + final folder = Directory(resultFolder); + + final decryption = + await cryptoRepository.decryptFile(file, folder.uri, sk.ok!); + + emit( + await decryption.foldAsync( + (_) async { + await loggerRepository.addLog( + const LogInfo('File successfully decrypted.\n'), + ); + return state.copyWith( + state: State.success, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during decryption.'), + ); + return state.copyWith( + state: State.failure, + error: error.message, + ); + }, + ), + ); + + return; + } } diff --git a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart index f518477..4b96dca 100644 --- a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart @@ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen { onPressed: () => bloc(context).decryptFromMemory(), ), ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'File', + style: AppTypography.title, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Encrypt file', + onPressed: () => bloc(context).encryptFile(), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Decrypt file', + onPressed: () => bloc(context).decryptFile(), + ), + ), const Padding( padding: EdgeInsets.all(8), child: Text( diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart index d4a0c5e..b007ce4 100644 --- a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart +++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart @@ -8,7 +8,9 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart'; import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; import 'package:native_crypto_example/domain/repositories/session_repository.dart'; part 'mode_switcher_state.dart'; @@ -16,8 +18,11 @@ part 'mode_switcher_state.dart'; class ModeSwitcherCubit extends Cubit { ModeSwitcherCubit( this.sessionRepository, + this.cryptoRepository, ) : super(const ModeSwitcherState(NativeCryptoMode())); + SessionRepository sessionRepository; + CryptoRepository cryptoRepository; FutureOr switchMode() async { final currentMode = await sessionRepository.getCurrentMode(); @@ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit { } sessionRepository.setCurrentMode(newMode); + if (cryptoRepository is CryptoRepositorySwitchableImpl) { + (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode; + } } else { newMode = const NativeCryptoMode(); sessionRepository.setCurrentMode(newMode); + + if (cryptoRepository is CryptoRepositorySwitchableImpl) { + (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode; + } } emit(ModeSwitcherState(newMode)); diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart index 991df1f..fbd378d 100644 --- a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart @@ -5,12 +5,12 @@ // https://opensource.org/licenses/MIT. import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart'; import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart'; import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart'; import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart'; import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart'; import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart'; -import 'package:native_crypto_example/presentation/home/widgets/blank.dart'; import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart'; import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; @@ -24,7 +24,7 @@ class HomeStateManagement HashStateManagement(), AESStateManagement(), TestVectorsStateManagement(), - const Blank() + const BenchmarkStateManagement(), ]; @override diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart index 1f51bbe..6b522a4 100644 --- a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart @@ -22,12 +22,12 @@ class AppBarStateManagement : 'PointyCastle', ), backgroundColor: state.currentMode.primaryColor, - // TODO(hpcl): enable mode switcher - // actions: [ - // Switch( - // value: state.currentMode == const NativeCryptoMode(), - // onChanged: (_) => bloc(context).switchMode(), - // ) - // ], + actions: [ + Switch( + activeColor: Colors.white, + value: state.currentMode == const NativeCryptoMode(), + onChanged: (_) => bloc(context).switchMode(), + ) + ], ); } diff --git a/packages/native_crypto/example/pubspec.yaml b/packages/native_crypto/example/pubspec.yaml index 73185a7..33e895d 100644 --- a/packages/native_crypto/example/pubspec.yaml +++ b/packages/native_crypto/example/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ version: 2.0.0 get_it: ^7.2.0 + file_picker: ^5.2.7 dev_dependencies: flutter_test: { sdk: flutter }