feat(api): update example with benchmark + file encryption
This commit is contained in:
parent
0bf72447a0
commit
7c8f7206f0
@ -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