v2 #12
| @ -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<void> 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<SecretKey> deriveKeyFromPassword( | ||||
|     String password, { | ||||
| @ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource { | ||||
|     return cipherText.bytes; | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<void> 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<SecretKey> generateSecureRandom(int length) async { | ||||
|     final SecretKey sk = await SecretKey.fromSecureRandom(length); | ||||
|  | ||||
| @ -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<void> decryptFile( | ||||
|     File cipherText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ) async { | ||||
|     throw UnimplementedError(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<SecretKey> deriveKeyFromPassword( | ||||
|     String password, { | ||||
| @ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { | ||||
| 
 | ||||
|   @override | ||||
|   Future<Uint8List> 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<void> encryptFile( | ||||
|     File plainText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ) async { | ||||
|     throw UnimplementedError(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<SecretKey> generateSecureRandom(int length) async { | ||||
|     if (_secureRandom == null) { | ||||
|  | ||||
| @ -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<void> 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<SecretKey> deriveKeyFromPassword( | ||||
|     String password, { | ||||
| @ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository { | ||||
| 
 | ||||
|   @override | ||||
|   FutureOrResult<Uint8List> 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<void> 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<SecretKey> generateSecureRandom(int length) => | ||||
|       Result.tryCatchAsync( | ||||
|  | ||||
| @ -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<Uint8List> 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<void> 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<SecretKey> 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<Uint8List> 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<Uint8List> 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<Uint8List> 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<Uint8List> 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<void> 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<SecretKey> generateSecureRandom(int length) => | ||||
|       Result.tryCatchAsync( | ||||
|         () async => cryptoDataSource.generateSecureRandom(length), | ||||
|         (error) { | ||||
|           if (error is NativeCryptoException) { | ||||
|             return ClientException('${error.message}'); | ||||
|           } | ||||
|           return ClientException(error.toString()); | ||||
|         }, | ||||
|       ); | ||||
| } | ||||
| @ -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<Uint8List> encrypt(Uint8List data, SecretKey key); | ||||
|   Future<void> encryptFile( | ||||
|     File plainText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ); | ||||
|   Future<Uint8List> encryptWithIV( | ||||
|     Uint8List data, | ||||
|     SecretKey key, | ||||
|     Uint8List iv, | ||||
|   ); | ||||
|   Future<Uint8List> decrypt(Uint8List data, SecretKey key); | ||||
|   Future<void> decryptFile( | ||||
|     File cipherText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ); | ||||
|   Future<Uint8List> hash(Hash hasher, Uint8List data); | ||||
|   Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key); | ||||
| } | ||||
|  | ||||
| @ -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<Uint8List> encrypt(Uint8List data, SecretKey key); | ||||
|   FutureOrResult<void> encryptFile( | ||||
|     File plainText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ); | ||||
|   FutureOrResult<Uint8List> encryptWithIV( | ||||
|     Uint8List data, | ||||
|     SecretKey key, | ||||
|     Uint8List iv, | ||||
|   ); | ||||
|   FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key); | ||||
|   FutureOrResult<void> decryptFile( | ||||
|     File cipherText, | ||||
|     Uri folderResult, | ||||
|     SecretKey key, | ||||
|   ); | ||||
| 
 | ||||
|   FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data); | ||||
|   FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key); | ||||
|  | ||||
| @ -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<NativeCryptoDataSourceImpl>(), | ||||
|     pointyCastleDataSource: getIt<PointyCastleDataSourceImpl>(), | ||||
|     currentMode: const NativeCryptoMode(), | ||||
|   ); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) => MultiProvider( | ||||
|         repositoryProviders: [ | ||||
| @ -35,18 +43,17 @@ class App extends StatelessWidget { | ||||
|           RepositoryProvider<SessionRepository>.value( | ||||
|             value: _sessionRepository, | ||||
|           ), | ||||
|           RepositoryProvider<CryptoRepository>( | ||||
|             create: (_) => CryptoRepositoryImpl( | ||||
|               cryptoDataSource: getIt<NativeCryptoDataSourceImpl>(), | ||||
|             ), | ||||
|           ), | ||||
|           RepositoryProvider<CryptoRepository>.value(value: _cryptoRepository), | ||||
|         ], | ||||
|         blocProviders: [ | ||||
|           BlocProvider<OutputCubit>( | ||||
|             create: (_) => OutputCubit(_loggerRepository), | ||||
|           ), | ||||
|           BlocProvider<ModeSwitcherCubit>( | ||||
|             create: (_) => ModeSwitcherCubit(_sessionRepository), | ||||
|             create: (_) => ModeSwitcherCubit( | ||||
|               _sessionRepository, | ||||
|               _cryptoRepository, | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|         child: MaterialApp( | ||||
|  | ||||
| @ -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<BenchmarkState> { | ||||
|   BenchmarkCubit({ | ||||
|     required this.sessionRepository, | ||||
|     required this.loggerRepository, | ||||
|     required this.cryptoRepository, | ||||
|   }) : super(const BenchmarkState.initial()); | ||||
|   final SessionRepository sessionRepository; | ||||
|   final LoggerRepository loggerRepository; | ||||
|   final CryptoRepository cryptoRepository; | ||||
| 
 | ||||
|   List<int> testedSizes = [ | ||||
|     2097152, | ||||
|     6291456, | ||||
|     10485760, | ||||
|     14680064, | ||||
|     18874368, | ||||
|     23068672, | ||||
|     27262976, | ||||
|     31457280, | ||||
|     35651584, | ||||
|     39845888, | ||||
|     44040192, | ||||
|     48234496, | ||||
|     52428800, | ||||
|   ]; | ||||
| 
 | ||||
|   FutureOr<void> 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; | ||||
|   } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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<BenchmarkCubit, BenchmarkState> { | ||||
|   const BenchmarkStateManagement({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   BenchmarkCubit create(BuildContext context) => BenchmarkCubit( | ||||
|         sessionRepository: repo<SessionRepository>(context), | ||||
|         loggerRepository: repo<LoggerRepository>(context), | ||||
|         cryptoRepository: repo<CryptoRepository>(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(), | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ); | ||||
| } | ||||
| @ -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<AESState> { | ||||
| 
 | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> 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<void> 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; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen<AESCubit, AESState> { | ||||
|               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( | ||||
|  | ||||
| @ -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<ModeSwitcherState> { | ||||
|   ModeSwitcherCubit( | ||||
|     this.sessionRepository, | ||||
|     this.cryptoRepository, | ||||
|   ) : super(const ModeSwitcherState(NativeCryptoMode())); | ||||
| 
 | ||||
|   SessionRepository sessionRepository; | ||||
|   CryptoRepository cryptoRepository; | ||||
| 
 | ||||
|   FutureOr<void> switchMode() async { | ||||
|     final currentMode = await sessionRepository.getCurrentMode(); | ||||
| @ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit<ModeSwitcherState> { | ||||
|       } | ||||
| 
 | ||||
|       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)); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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(), | ||||
|           ) | ||||
|         ], | ||||
|       ); | ||||
| } | ||||
|  | ||||
| @ -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 } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user