feat(api): update example with benchmark + file encryption

This commit is contained in:
Hugo Pointcheval 2023-04-05 15:17:56 +02:00
parent 0bf72447a0
commit 7c8f7206f0
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
16 changed files with 751 additions and 18 deletions

View File

@ -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);

View File

@ -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) {

View File

@ -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(

View File

@ -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());
},
);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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(

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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(),
),
),
],
);
}

View File

@ -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;
}
}

View File

@ -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(

View File

@ -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));

View File

@ -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

View File

@ -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(),
)
],
);
}

View File

@ -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 }