feat(api): update example
This commit is contained in:
parent
2f22cc549d
commit
e47004e2d0
@ -1,10 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: cf4400006550b70f28e4b4af815151d1e74846c6
|
||||
revision: cd41fdd495f6944ecd3506c21e94c6567b073278
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
|
||||
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
|
||||
- platform: web
|
||||
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
|
||||
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
@ -1,29 +1 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
@ -57,6 +57,7 @@ android {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
namespace 'fr.pointcheval.native_crypto_example'
|
||||
}
|
||||
|
||||
flutter {
|
||||
|
@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.pointcheval.native_crypto_example">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.pointcheval.native_crypto_example">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="native_crypto_example"
|
||||
android:name="${applicationName}"
|
||||
|
@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.pointcheval.native_crypto_example">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
@ -6,7 +6,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||
|
@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
<string>11.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -14,7 +14,7 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/native_crypto_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
|
||||
|
||||
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
||||
|
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -200,6 +200,7 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@ -214,6 +215,7 @@
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@ -340,7 +342,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@ -418,7 +420,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@ -467,7 +469,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
@ -45,5 +45,7 @@
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
35
packages/native_crypto/example/lib/core/get_it.dart
Normal file
35
packages/native_crypto/example/lib/core/get_it.dart
Normal file
@ -0,0 +1,35 @@
|
||||
// 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:get_it/get_it.dart';
|
||||
import 'package:native_crypto_example/data/data_sources/logger_data_source_impl.dart';
|
||||
import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart';
|
||||
import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
|
||||
import 'package:native_crypto_example/data/data_sources/session_data_source_impl.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
|
||||
|
||||
final getIt = GetIt.I;
|
||||
|
||||
abstract class GetItInitializer {
|
||||
static Future<void> init() async {
|
||||
getIt
|
||||
..registerLazySingleton<SessionDataSource>(
|
||||
SessionDataSourceImpl.new,
|
||||
)
|
||||
..registerLazySingleton<LoggerDataSource>(
|
||||
LoggerDataSourceImpl.new,
|
||||
)
|
||||
..registerLazySingleton<NativeCryptoDataSourceImpl>(
|
||||
NativeCryptoDataSourceImpl.new,
|
||||
)
|
||||
..registerLazySingleton<PointyCastleDataSourceImpl>(
|
||||
PointyCastleDataSourceImpl.new,
|
||||
);
|
||||
|
||||
await getIt.allReady();
|
||||
}
|
||||
}
|
18
packages/native_crypto/example/lib/core/typography.dart
Normal file
18
packages/native_crypto/example/lib/core/typography.dart
Normal file
@ -0,0 +1,18 @@
|
||||
// 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';
|
||||
|
||||
abstract class AppTypography {
|
||||
static const title = TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 24,
|
||||
);
|
||||
|
||||
static const body = TextStyle(
|
||||
fontSize: 16,
|
||||
);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// 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:native_crypto_example/domain/data_sources/logger_data_source.dart';
|
||||
import 'package:native_crypto_example/domain/entities/log_message.dart';
|
||||
|
||||
class LoggerDataSourceImpl extends LoggerDataSource {
|
||||
final Map<DateTime, LogMessage> _logs = {};
|
||||
final StreamController<Map<DateTime, LogMessage>> _streamController =
|
||||
StreamController.broadcast();
|
||||
|
||||
@override
|
||||
Future<void> addLog(LogMessage message) async {
|
||||
_logs[DateTime.now()] = message;
|
||||
_streamController.add(Map.from(_logs));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearLog() async {
|
||||
_logs.clear();
|
||||
_streamController.add(Map.from(_logs));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<DateTime, LogMessage>> getLogs() async => _logs;
|
||||
|
||||
@override
|
||||
Stream<Map<DateTime, LogMessage>> streamLogs() => _streamController.stream;
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
// 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/foundation.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
|
||||
|
||||
class NativeCryptoDataSourceImpl extends CryptoDataSource {
|
||||
@override
|
||||
Future<Uint8List> decrypt(Uint8List data, SecretKey key) async {
|
||||
final AES cipher = AES(
|
||||
key: key,
|
||||
mode: AESMode.gcm,
|
||||
padding: AESPadding.none,
|
||||
);
|
||||
final Uint8List plainText = await cipher.decrypt(
|
||||
CipherText(
|
||||
data,
|
||||
chunkFactory: (bytes) => AESCipherChunk(
|
||||
bytes,
|
||||
ivLength: cipher.mode.ivLength,
|
||||
tagLength: cipher.mode.tagLength,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return plainText;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SecretKey> deriveKeyFromPassword(
|
||||
String password, {
|
||||
required String salt,
|
||||
}) async {
|
||||
final Pbkdf2 pbkdf2 = Pbkdf2(
|
||||
length: 32,
|
||||
iterations: 1000,
|
||||
salt: salt.toBytes(),
|
||||
hashAlgorithm: HashAlgorithm.sha256,
|
||||
);
|
||||
return pbkdf2(password: password);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> hash(Hash hasher, Uint8List data) async {
|
||||
final Uint8List digestMessage = await hasher.digest(data);
|
||||
|
||||
return digestMessage;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key) async {
|
||||
final Uint8List digestMessage = await hmac.digest(data, key);
|
||||
|
||||
return digestMessage;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> encrypt(Uint8List data, SecretKey key) async {
|
||||
final AES cipher = AES(
|
||||
key: key,
|
||||
mode: AESMode.gcm,
|
||||
padding: AESPadding.none,
|
||||
);
|
||||
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(data);
|
||||
|
||||
return cipherText.bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> encryptWithIV(
|
||||
Uint8List data,
|
||||
SecretKey key,
|
||||
Uint8List iv,
|
||||
) async {
|
||||
final AES cipher = AES(
|
||||
key: key,
|
||||
mode: AESMode.gcm,
|
||||
padding: AESPadding.none,
|
||||
);
|
||||
|
||||
final AESCipherChunk chunk = await cipher.encryptWithIV(data, iv);
|
||||
final CipherText<AESCipherChunk> cipherText = CipherText.fromChunks(
|
||||
[chunk],
|
||||
chunkFactory: (bytes) => AESCipherChunk(
|
||||
bytes,
|
||||
ivLength: cipher.mode.ivLength,
|
||||
tagLength: cipher.mode.tagLength,
|
||||
),
|
||||
);
|
||||
|
||||
return cipherText.bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SecretKey> generateSecureRandom(int length) async {
|
||||
final SecretKey sk = await SecretKey.fromSecureRandom(length);
|
||||
|
||||
return sk;
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
// 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.
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
|
||||
import 'package:pointycastle/export.dart';
|
||||
import 'package:pointycastle/src/platform_check/platform_check.dart';
|
||||
|
||||
class PointyCastleDataSourceImpl extends CryptoDataSource {
|
||||
FortunaRandom? _secureRandom;
|
||||
|
||||
@override
|
||||
Future<Uint8List> decrypt(Uint8List data, SecretKey key) async {
|
||||
final iv = Uint8List.fromList(data.sublist(0, 12));
|
||||
final cipherTextWithoutIv = Uint8List.fromList(
|
||||
data.sublist(12),
|
||||
);
|
||||
|
||||
final gcm = GCMBlockCipher(AESEngine())
|
||||
..init(
|
||||
false,
|
||||
AEADParameters(
|
||||
KeyParameter(key.bytes),
|
||||
16 * 8,
|
||||
iv,
|
||||
Uint8List(0),
|
||||
),
|
||||
);
|
||||
final paddedPlainText = gcm.process(cipherTextWithoutIv);
|
||||
|
||||
return paddedPlainText;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SecretKey> deriveKeyFromPassword(
|
||||
String password, {
|
||||
required String salt,
|
||||
}) async {
|
||||
final derivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64))
|
||||
..init(
|
||||
Pbkdf2Parameters(salt.toBytes(), 1000, 32),
|
||||
);
|
||||
|
||||
final Uint8List sk = derivator.process(password.toBytes());
|
||||
|
||||
return SecretKey(sk);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> hash(Hash hasher, Uint8List data) async {
|
||||
final Digest? digest;
|
||||
|
||||
switch (hasher.runtimeType) {
|
||||
case Sha256:
|
||||
digest = SHA256Digest();
|
||||
break;
|
||||
case Sha384:
|
||||
digest = SHA384Digest();
|
||||
break;
|
||||
case Sha512:
|
||||
digest = SHA512Digest();
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'Unsupported hash algorithm: ${hasher.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
return digest.process(data);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key) async {
|
||||
final HMac? digest;
|
||||
|
||||
switch (hmac.runtimeType) {
|
||||
case HmacSha256:
|
||||
digest = HMac.withDigest(SHA256Digest());
|
||||
digest.init(KeyParameter(key.bytes));
|
||||
break;
|
||||
case Sha384:
|
||||
digest = HMac.withDigest(SHA384Digest());
|
||||
digest.init(KeyParameter(key.bytes));
|
||||
break;
|
||||
case Sha512:
|
||||
digest = HMac.withDigest(SHA512Digest());
|
||||
digest.init(KeyParameter(key.bytes));
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'Unsupported hmac algorithm: ${hmac.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
return digest.process(data);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> encrypt(Uint8List data, SecretKey key) async {
|
||||
final iv = (await generateSecureRandom(12 * 8)).bytes;
|
||||
|
||||
final gcm = GCMBlockCipher(AESEngine())
|
||||
..init(
|
||||
true,
|
||||
AEADParameters(
|
||||
KeyParameter(key.bytes),
|
||||
16 * 8,
|
||||
iv,
|
||||
Uint8List(0),
|
||||
),
|
||||
);
|
||||
|
||||
final cipherText = gcm.process(data);
|
||||
|
||||
return Uint8List.fromList(
|
||||
iv + cipherText,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> encryptWithIV(
|
||||
Uint8List data, SecretKey key, Uint8List iv,) async {
|
||||
final gcm = GCMBlockCipher(AESEngine())
|
||||
..init(
|
||||
true,
|
||||
AEADParameters(
|
||||
KeyParameter(key.bytes),
|
||||
16 * 8,
|
||||
iv,
|
||||
Uint8List(0),
|
||||
),
|
||||
);
|
||||
|
||||
final cipherText = gcm.process(data);
|
||||
|
||||
return Uint8List.fromList(
|
||||
iv + cipherText,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SecretKey> generateSecureRandom(int length) async {
|
||||
if (_secureRandom == null) {
|
||||
_secureRandom = FortunaRandom();
|
||||
_secureRandom!.seed(
|
||||
KeyParameter(Platform.instance.platformEntropySource().getBytes(32)),
|
||||
);
|
||||
}
|
||||
|
||||
final sk = _secureRandom!.nextBytes(length);
|
||||
|
||||
return SecretKey(sk);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// 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:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
|
||||
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||
|
||||
class SessionDataSourceImpl extends SessionDataSource {
|
||||
SecretKey? _sk;
|
||||
Mode? _mode = const NativeCryptoMode();
|
||||
|
||||
@override
|
||||
Future<SecretKey> getSessionKey() async {
|
||||
if (_sk == null) {
|
||||
throw Exception('Session key is not ready');
|
||||
}
|
||||
|
||||
return _sk!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isSessionKeyReady() async => _sk != null;
|
||||
|
||||
@override
|
||||
Future<void> setSessionKey(SecretKey key) async {
|
||||
_sk = key;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Mode> getCurrentMode() async {
|
||||
if (_mode == null) {
|
||||
throw Exception('Mode is not set');
|
||||
}
|
||||
|
||||
return _mode!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setCurrentMode(Mode mode) async {
|
||||
_mode = mode;
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
// 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: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/repositories/crypto_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class CryptoRepositoryImpl extends CryptoRepository {
|
||||
CryptoRepositoryImpl({
|
||||
required this.cryptoDataSource,
|
||||
});
|
||||
CryptoDataSource cryptoDataSource;
|
||||
|
||||
@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<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<SecretKey> generateSecureRandom(int length) =>
|
||||
Result.tryCatchAsync(
|
||||
() async => cryptoDataSource.generateSecureRandom(length),
|
||||
(error) {
|
||||
if (error is NativeCryptoException) {
|
||||
return ClientException('${error.message}');
|
||||
}
|
||||
return ClientException(error.toString());
|
||||
},
|
||||
);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// 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:native_crypto_example/domain/data_sources/logger_data_source.dart';
|
||||
import 'package:native_crypto_example/domain/entities/log_message.dart';
|
||||
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class LoggerRepositoryImpl extends LoggerRepository {
|
||||
LoggerRepositoryImpl({
|
||||
required this.loggerDataSource,
|
||||
});
|
||||
|
||||
final LoggerDataSource loggerDataSource;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> addLog(LogMessage message) => Result.tryCatchAsync(
|
||||
() async => loggerDataSource.addLog(message),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<void> clearLog() => Result.tryCatchAsync(
|
||||
() async => loggerDataSource.clearLog(),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<Map<DateTime, LogMessage>> getLogs() => Result.tryCatchAsync(
|
||||
() async => loggerDataSource.getLogs(),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
StreamResult<Map<DateTime, LogMessage>> streamLogs() =>
|
||||
loggerDataSource.streamLogs().map(Ok.new);
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// 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:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
|
||||
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class SessionRepositoryImpl extends SessionRepository {
|
||||
SessionRepositoryImpl({
|
||||
required this.sessionDataSource,
|
||||
});
|
||||
SessionDataSource sessionDataSource;
|
||||
|
||||
@override
|
||||
FutureOrResult<SecretKey> getSessionKey() => Result.tryCatchAsync(
|
||||
() async => sessionDataSource.getSessionKey(),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<bool> isSessionKeyReady() => Result.tryCatchAsync(
|
||||
() async => sessionDataSource.isSessionKeyReady(),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<void> setSessionKey(SecretKey key) => Result.tryCatchAsync(
|
||||
() async => sessionDataSource.setSessionKey(key),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<Mode> getCurrentMode() => Result.tryCatchAsync(
|
||||
() async => sessionDataSource.getCurrentMode(),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<void> setCurrentMode(Mode mode) => Result.tryCatchAsync(
|
||||
() async => sessionDataSource.setCurrentMode(mode),
|
||||
(error) => ClientException(error.toString()),
|
||||
);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// 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/foundation.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class CryptoDataSource extends BaseDataSource {
|
||||
Future<SecretKey> generateSecureRandom(int length);
|
||||
Future<SecretKey> deriveKeyFromPassword(
|
||||
String password, {
|
||||
required String salt,
|
||||
});
|
||||
Future<Uint8List> encrypt(Uint8List data, SecretKey key);
|
||||
Future<Uint8List> encryptWithIV(
|
||||
Uint8List data,
|
||||
SecretKey key,
|
||||
Uint8List iv,
|
||||
);
|
||||
Future<Uint8List> decrypt(Uint8List data, SecretKey key);
|
||||
Future<Uint8List> hash(Hash hasher, Uint8List data);
|
||||
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// 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:native_crypto_example/domain/entities/log_message.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class LoggerDataSource extends BaseDataSource {
|
||||
Future<void> addLog(LogMessage message);
|
||||
Future<void> clearLog();
|
||||
Future<Map<DateTime, LogMessage>> getLogs();
|
||||
Stream<Map<DateTime, LogMessage>> streamLogs();
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// 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:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class SessionDataSource extends BaseDataSource {
|
||||
Future<void> setSessionKey(SecretKey key);
|
||||
Future<bool> isSessionKeyReady();
|
||||
Future<SecretKey> getSessionKey();
|
||||
Future<Mode> getCurrentMode();
|
||||
Future<void> setCurrentMode(Mode mode);
|
||||
}
|
@ -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.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LogMessage {
|
||||
const LogMessage(this.prefix, this.color, this.message);
|
||||
|
||||
final String prefix;
|
||||
final Color color;
|
||||
final String message;
|
||||
}
|
||||
|
||||
class LogInfo extends LogMessage {
|
||||
const LogInfo(String message) : super('info', Colors.black, message);
|
||||
}
|
||||
|
||||
class LogWarning extends LogMessage {
|
||||
const LogWarning(String message) : super('warn', Colors.orange, message);
|
||||
}
|
||||
|
||||
class LogError extends LogMessage {
|
||||
const LogError(String message) : super('fail', Colors.red, message);
|
||||
}
|
34
packages/native_crypto/example/lib/domain/entities/mode.dart
Normal file
34
packages/native_crypto/example/lib/domain/entities/mode.dart
Normal file
@ -0,0 +1,34 @@
|
||||
// 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:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
class Mode extends Entity {
|
||||
const Mode(
|
||||
this.primaryColor,
|
||||
this.secondaryColor,
|
||||
);
|
||||
|
||||
final Color primaryColor;
|
||||
final Color secondaryColor;
|
||||
}
|
||||
|
||||
class NativeCryptoMode extends Mode {
|
||||
const NativeCryptoMode()
|
||||
: super(
|
||||
Colors.blue,
|
||||
Colors.black,
|
||||
);
|
||||
}
|
||||
|
||||
class PointyCastleMode extends Mode {
|
||||
const PointyCastleMode()
|
||||
: super(
|
||||
Colors.red,
|
||||
Colors.white,
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// 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.
|
||||
|
||||
enum State { initial, loading, success, failure }
|
@ -0,0 +1,54 @@
|
||||
// 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/foundation.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
|
||||
class TestVector {
|
||||
TestVector({
|
||||
required this.key,
|
||||
required this.nonce,
|
||||
required this.plainText,
|
||||
required this.cipherText,
|
||||
required this.tag,
|
||||
});
|
||||
|
||||
final String key;
|
||||
final String nonce;
|
||||
final String plainText;
|
||||
final String cipherText;
|
||||
final String tag;
|
||||
|
||||
Uint8List get keyBytes => key.toBytes(from: Encoding.base16);
|
||||
Uint8List get nonceBytes => nonce.toBytes(from: Encoding.base16);
|
||||
Uint8List get tagBytes => tag.toBytes(from: Encoding.base16);
|
||||
Uint8List get plainTextBytes => plainText.toBytes(from: Encoding.base16);
|
||||
Uint8List get cipherTextBytes {
|
||||
final iv = nonceBytes;
|
||||
final data = cipherText.toBytes(from: Encoding.base16);
|
||||
final tag = tagBytes;
|
||||
return Uint8List.fromList(iv + data + tag);
|
||||
}
|
||||
|
||||
bool validateCipherText(Uint8List? testCipherText) {
|
||||
final result = listEquals(testCipherText, cipherTextBytes);
|
||||
if (!result) {
|
||||
debugPrint('Cipher texts differs:\n$cipherTextBytes\n$testCipherText');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validatePlainText(Uint8List? testPlainText) {
|
||||
final result = listEquals(testPlainText, plainTextBytes);
|
||||
if (!result) {
|
||||
debugPrint('Plain texts differs:\n$plainTextBytes\n$testPlainText');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validate(Uint8List? testPlainText, Uint8List? testCipherText) =>
|
||||
validateCipherText(testCipherText) && validatePlainText(testPlainText);
|
||||
}
|
@ -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.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class CryptoRepository extends BaseRepository {
|
||||
FutureOrResult<SecretKey> generateSecureRandom(int length);
|
||||
FutureOrResult<SecretKey> deriveKeyFromPassword(
|
||||
String password, {
|
||||
required String salt,
|
||||
});
|
||||
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key);
|
||||
FutureOrResult<Uint8List> encryptWithIV(
|
||||
Uint8List data,
|
||||
SecretKey key,
|
||||
Uint8List iv,
|
||||
);
|
||||
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key);
|
||||
|
||||
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data);
|
||||
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: logger_repository.dart
|
||||
// Created Date: 09/01/2023 22:12:56
|
||||
// Last Modified: 09/01/2023 23:03:51
|
||||
// -----
|
||||
// Copyright (c) 2023
|
||||
|
||||
import 'package:native_crypto_example/domain/entities/log_message.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class LoggerRepository extends BaseRepository {
|
||||
FutureOrResult<void> addLog(LogMessage message);
|
||||
FutureOrResult<void> clearLog();
|
||||
FutureOrResult<Map<DateTime, LogMessage>> getLogs();
|
||||
StreamResult<Map<DateTime, LogMessage>> streamLogs();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: session_repository.dart
|
||||
// Created Date: 08/01/2023 17:35:46
|
||||
// Last Modified: 09/01/2023 22:13:21
|
||||
// -----
|
||||
// Copyright (c) 2023
|
||||
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
abstract class SessionRepository extends BaseRepository {
|
||||
FutureOrResult<void> setSessionKey(SecretKey key);
|
||||
FutureOrResult<bool> isSessionKeyReady();
|
||||
FutureOrResult<SecretKey> getSessionKey();
|
||||
FutureOrResult<Mode> getCurrentMode();
|
||||
FutureOrResult<void> setCurrentMode(Mode mode);
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: home.dart
|
||||
// Created Date: 28/12/2021 13:48:36
|
||||
// Last Modified: 28/12/2021 15:18:03
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:native_crypto_example/pages/benchmark_page.dart';
|
||||
|
||||
import 'pages/cipher_page.dart';
|
||||
import 'pages/kdf_page.dart';
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
const Home({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
int _currentIndex = 0;
|
||||
final List<Widget> _children = [KdfPage(), CipherPage(), BenchmarkPage()];
|
||||
|
||||
void onTabTapped(int index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Native Crypto'),
|
||||
),
|
||||
body: _children[_currentIndex],
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
selectedItemColor: Colors.blue,
|
||||
unselectedItemColor: Colors.black,
|
||||
showUnselectedLabels: true,
|
||||
onTap: onTabTapped, // new
|
||||
currentIndex: _currentIndex, // new
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.vpn_key),
|
||||
label: 'Key',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.lock),
|
||||
label: 'Encryption',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.timer),
|
||||
label: 'Benchmark',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,23 +3,16 @@
|
||||
// -----
|
||||
// File: main.dart
|
||||
// Created Date: 27/12/2021 21:15:12
|
||||
// Last Modified: 28/12/2021 13:51:36
|
||||
// Last Modified: 10/01/2023 14:59:05
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:native_crypto_example/home.dart';
|
||||
import 'package:native_crypto_example/core/get_it.dart';
|
||||
import 'package:native_crypto_example/presentation/app/app.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const ProviderScope(child: MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(home: Home());
|
||||
}
|
||||
Future<void> main() async {
|
||||
await GetItInitializer.init();
|
||||
|
||||
runApp(App());
|
||||
}
|
||||
|
@ -1,189 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: benchmark_page.dart
|
||||
// Created Date: 28/12/2021 15:12:39
|
||||
// Last Modified: 26/05/2022 20:19:28
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto_example/pointycastle/aes_gcm.dart';
|
||||
import 'package:native_crypto_example/widgets/button.dart';
|
||||
|
||||
import '../session.dart';
|
||||
import '../widgets/output.dart';
|
||||
|
||||
class BenchmarkPage extends ConsumerWidget {
|
||||
BenchmarkPage({Key? key}) : super(key: key);
|
||||
|
||||
final Output keyContent = Output();
|
||||
final Output benchmarkStatus = Output(large: true);
|
||||
|
||||
Future<void> _benchmark(
|
||||
WidgetRef ref,
|
||||
Cipher cipher, {
|
||||
bool usePc = false,
|
||||
bool encryptionOnly = false,
|
||||
}) async {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
AesGcm pc = AesGcm();
|
||||
|
||||
if (state.secretKey.bytes.isEmpty) {
|
||||
benchmarkStatus
|
||||
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||
return;
|
||||
}
|
||||
|
||||
List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
|
||||
int multiplier = pow(2, 20).toInt(); // MiB
|
||||
|
||||
benchmarkStatus.print("[Benchmark] Sizes: ${testedSizes.join('/')}MiB\n");
|
||||
benchmarkStatus.appendln(
|
||||
"[Benchmark] Engine: " + (usePc ? " PointyCastle" : " NativeCrypto"));
|
||||
benchmarkStatus.appendln("[Benchmark] Test: " +
|
||||
(encryptionOnly ? " Encryption Only" : " Encryption & Decryption"));
|
||||
benchmarkStatus.appendln(
|
||||
'[Benchmark] bytesCountPerChunk: ${Cipher.bytesCountPerChunk} bytes/chunk');
|
||||
|
||||
String csv = encryptionOnly
|
||||
? "Run;Size (B);Encryption Time (ms)\n"
|
||||
: "Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n";
|
||||
|
||||
int run = 0;
|
||||
var beforeBench = DateTime.now();
|
||||
|
||||
for (int size in testedSizes) {
|
||||
run++;
|
||||
final StringBuffer csvLine = StringBuffer();
|
||||
final dummyBytes = Uint8List(size * multiplier);
|
||||
csvLine.write('$run;${size * multiplier};');
|
||||
|
||||
// Encryption
|
||||
Object encryptedBigFile;
|
||||
var before = DateTime.now();
|
||||
if (usePc) {
|
||||
encryptedBigFile = pc.encrypt(dummyBytes, state.secretKey.bytes);
|
||||
} else {
|
||||
encryptedBigFile = await cipher.encrypt(dummyBytes);
|
||||
}
|
||||
var after = DateTime.now();
|
||||
|
||||
var benchmark =
|
||||
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
|
||||
benchmarkStatus
|
||||
.appendln('[Benchmark] ${size}MiB => Encryption took $benchmark ms');
|
||||
csvLine.write('$benchmark');
|
||||
|
||||
if (!encryptionOnly) {
|
||||
// Decryption
|
||||
before = DateTime.now();
|
||||
if (usePc) {
|
||||
pc.decrypt(encryptedBigFile as Uint8List, state.secretKey.bytes);
|
||||
} else {
|
||||
await cipher.decrypt(encryptedBigFile as CipherTextWrapper);
|
||||
}
|
||||
after = DateTime.now();
|
||||
benchmark =
|
||||
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
|
||||
benchmarkStatus.appendln(
|
||||
'[Benchmark] ${size}MiB => Decryption took $benchmark ms');
|
||||
csvLine.write(';$benchmark');
|
||||
}
|
||||
csv += csvLine.toString() + '\n';
|
||||
}
|
||||
var afterBench = DateTime.now();
|
||||
var benchmark =
|
||||
afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch;
|
||||
var sum = testedSizes.reduce((a, b) => a + b);
|
||||
benchmarkStatus
|
||||
.appendln('[Benchmark] Finished: ${sum}MiB in $benchmark ms');
|
||||
benchmarkStatus.appendln('[Benchmark] Check the console for csv data');
|
||||
benchmarkStatus.appendln(csv);
|
||||
debugPrint(csv);
|
||||
}
|
||||
|
||||
void _clear() {
|
||||
benchmarkStatus.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
if (state.secretKey.bytes.isEmpty) {
|
||||
keyContent
|
||||
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Align(
|
||||
child: Text("Secret Key"),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
keyContent,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
keyContent.print(state.secretKey.bytes.toString());
|
||||
|
||||
AES cipher = AES(state.secretKey);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Align(
|
||||
child: Text("Secret Key"),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
keyContent,
|
||||
Wrap(
|
||||
children: [
|
||||
Button(
|
||||
() => _benchmark(ref, cipher),
|
||||
"NativeCrypto",
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
() => _benchmark(ref, cipher, usePc: true),
|
||||
"PointyCastle",
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
() => _benchmark(ref, cipher, encryptionOnly: true),
|
||||
"NC Encryption Only",
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
() => _benchmark(
|
||||
ref,
|
||||
cipher,
|
||||
usePc: true,
|
||||
encryptionOnly: true,
|
||||
),
|
||||
"PC Encryption Only",
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Button(
|
||||
_clear,
|
||||
"Clear",
|
||||
),
|
||||
],
|
||||
),
|
||||
benchmarkStatus,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: cipher_page.dart
|
||||
// Created Date: 28/12/2021 13:33:15
|
||||
// Last Modified: 27/05/2022 16:42:10
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto/native_crypto_ext.dart';
|
||||
import 'package:native_crypto_example/widgets/button.dart';
|
||||
|
||||
import '../session.dart';
|
||||
import '../widgets/output.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class CipherPage extends ConsumerWidget {
|
||||
CipherPage({Key? key}) : super(key: key);
|
||||
|
||||
final Output keyContent = Output();
|
||||
final Output encryptionStatus = Output();
|
||||
final Output decryptionStatus = Output();
|
||||
|
||||
final TextEditingController _plainTextController = TextEditingController()
|
||||
..text = 'PlainText';
|
||||
CipherTextWrapper? cipherText;
|
||||
|
||||
Future<void> _encrypt(WidgetRef ref, Cipher cipher) async {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
final plainText = _plainTextController.text.trim();
|
||||
|
||||
if (state.secretKey.bytes.isEmpty) {
|
||||
encryptionStatus
|
||||
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||
} else if (plainText.isEmpty) {
|
||||
encryptionStatus.print('Entry is empty');
|
||||
} else {
|
||||
var stringToBytes = plainText.toBytes();
|
||||
cipherText = await cipher.encrypt(stringToBytes);
|
||||
encryptionStatus.print('String successfully encrypted:\n');
|
||||
|
||||
CipherText unwrap = cipherText!.unwrap<CipherText>();
|
||||
encryptionStatus.append(unwrap.base16);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _alter() async {
|
||||
if (cipherText == null) {
|
||||
decryptionStatus.print('Encrypt before altering CipherText!');
|
||||
} else {
|
||||
// Add 1 to the first byte
|
||||
Uint8List _altered = cipherText!.unwrap<CipherText>().bytes;
|
||||
_altered[0] += 1;
|
||||
// Recreate cipher text with altered data
|
||||
cipherText = CipherTextWrapper.fromBytes(
|
||||
_altered,
|
||||
ivLength: AESMode.gcm.ivLength,
|
||||
tagLength: AESMode.gcm.tagLength,
|
||||
);
|
||||
encryptionStatus.print('String successfully encrypted:\n');
|
||||
|
||||
CipherText unwrap = cipherText!.unwrap();
|
||||
encryptionStatus.appendln(unwrap.base16);
|
||||
decryptionStatus.print('CipherText altered!\nDecryption will fail.');
|
||||
}
|
||||
}
|
||||
|
||||
void _decrypt(WidgetRef ref, Cipher cipher) async {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
|
||||
if (state.secretKey.bytes.isEmpty) {
|
||||
decryptionStatus
|
||||
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||
} else if (cipherText == null) {
|
||||
decryptionStatus.print('Encrypt before decrypting!');
|
||||
} else {
|
||||
try {
|
||||
Uint8List plainText = await cipher.decrypt(cipherText!);
|
||||
var bytesToString = plainText.toStr();
|
||||
decryptionStatus
|
||||
.print('String successfully decrypted:\n\n$bytesToString');
|
||||
} on NativeCryptoException catch (e) {
|
||||
decryptionStatus.print(e.message ?? 'Decryption failed!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
if (state.secretKey.bytes.isEmpty) {
|
||||
keyContent
|
||||
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Align(
|
||||
child: Text("Secret Key"),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
keyContent,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
keyContent.print(state.secretKey.bytes.toString());
|
||||
|
||||
AES cipher = AES(state.secretKey);
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Align(
|
||||
child: Text("Secret Key"),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
keyContent,
|
||||
TextField(
|
||||
controller: _plainTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Plain text',
|
||||
),
|
||||
),
|
||||
Button(
|
||||
() => _encrypt(ref, cipher),
|
||||
"Encrypt",
|
||||
),
|
||||
encryptionStatus,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Button(
|
||||
_alter,
|
||||
"Alter cipher",
|
||||
),
|
||||
Button(
|
||||
() => _decrypt(ref, cipher),
|
||||
"Decrypt",
|
||||
),
|
||||
],
|
||||
),
|
||||
decryptionStatus,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: kdf_page.dart
|
||||
// Created Date: 28/12/2021 13:40:34
|
||||
// Last Modified: 26/05/2022 21:09:47
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
import 'package:native_crypto/native_crypto_ext.dart';
|
||||
import 'package:native_crypto_example/widgets/button.dart';
|
||||
|
||||
import '../session.dart';
|
||||
import '../widgets/output.dart';
|
||||
|
||||
class KdfPage extends ConsumerWidget {
|
||||
KdfPage({Key? key}) : super(key: key);
|
||||
|
||||
final Output keyContent = Output();
|
||||
final Output keyStatus = Output();
|
||||
final Output pbkdf2Status = Output();
|
||||
final Output hashStatus = Output(large: true);
|
||||
|
||||
final TextEditingController _pwdTextController = TextEditingController()
|
||||
..text = 'Password';
|
||||
final TextEditingController _messageTextController = TextEditingController()
|
||||
..text = 'Message';
|
||||
|
||||
Future<void> _generate(WidgetRef ref) async {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
try {
|
||||
SecretKey sk = await SecretKey.fromSecureRandom(256);
|
||||
state.setKey(sk);
|
||||
keyStatus.print(
|
||||
"SecretKey successfully generated.\nLength: ${state.secretKey.bytes.length} bytes");
|
||||
keyContent.print(state.secretKey.bytes.toString());
|
||||
debugPrint("As hex :\n${sk.base16}");
|
||||
} catch (e) {
|
||||
keyStatus.print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pbkdf2(WidgetRef ref) async {
|
||||
Session state = ref.read(sessionProvider.state).state;
|
||||
final password = _pwdTextController.text.trim();
|
||||
|
||||
if (password.isEmpty) {
|
||||
pbkdf2Status.print('Password is empty');
|
||||
} else {
|
||||
Pbkdf2 _pbkdf2 = Pbkdf2(
|
||||
keyBytesCount: 32,
|
||||
iterations: 1000,
|
||||
algorithm: HashAlgorithm.sha512,
|
||||
);
|
||||
SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt');
|
||||
state.setKey(sk);
|
||||
pbkdf2Status.print('Key successfully derived.');
|
||||
keyContent.print(state.secretKey.bytes.toString());
|
||||
debugPrint("As hex :\n${sk.base16}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _hash(HashAlgorithm hasher) async {
|
||||
final message = _messageTextController.text.trim();
|
||||
if (message.isEmpty) {
|
||||
hashStatus.print('Message is empty');
|
||||
} else {
|
||||
Uint8List hash = await hasher.digest(message.toBytes());
|
||||
hashStatus.print(
|
||||
'Message successfully hashed with $hasher :${hash.toStr(to: Encoding.base16)}');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Align(
|
||||
child: Text("SecretKey"),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
keyContent,
|
||||
Button(
|
||||
() => _generate(ref),
|
||||
"Generate SecretKey",
|
||||
),
|
||||
keyStatus,
|
||||
TextField(
|
||||
controller: _pwdTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Password',
|
||||
),
|
||||
),
|
||||
Button(
|
||||
() => _pbkdf2(ref),
|
||||
"Apply PBKDF2",
|
||||
),
|
||||
pbkdf2Status,
|
||||
TextField(
|
||||
controller: _messageTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Message',
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Button(
|
||||
() => _hash(HashAlgorithm.sha256),
|
||||
"SHA256",
|
||||
),
|
||||
Button(
|
||||
() => _hash(HashAlgorithm.sha384),
|
||||
"SHA384",
|
||||
),
|
||||
Button(
|
||||
() => _hash(HashAlgorithm.sha512),
|
||||
"SHA512",
|
||||
),
|
||||
],
|
||||
),
|
||||
hashStatus,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: aes_gcm.dart
|
||||
// Created Date: 24/05/2022 16:34:54
|
||||
// Last Modified: 27/05/2022 17:36:31
|
||||
// -----
|
||||
// Copyright (c) 2022
|
||||
// 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.
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
@ -69,7 +66,8 @@ class AesGcm {
|
||||
|
||||
_secureRandom = FortunaRandom();
|
||||
_secureRandom!.seed(
|
||||
KeyParameter(Platform.instance.platformEntropySource().getBytes(32)));
|
||||
KeyParameter(Platform.instance.platformEntropySource().getBytes(32)),
|
||||
);
|
||||
}
|
||||
|
||||
// Use it to generate the random bytes
|
||||
|
58
packages/native_crypto/example/lib/presentation/app/app.dart
Normal file
58
packages/native_crypto/example/lib/presentation/app/app.dart
Normal file
@ -0,0 +1,58 @@
|
||||
// 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: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/repositories/logger_repository_impl.dart';
|
||||
import 'package:native_crypto_example/data/repositories/session_repository_impl.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/home/blocs/mode_switcher/mode_switcher_cubit.dart';
|
||||
import 'package:native_crypto_example/presentation/home/state_management/home_state_management.dart';
|
||||
import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
App({super.key});
|
||||
|
||||
final LoggerRepository _loggerRepository =
|
||||
LoggerRepositoryImpl(loggerDataSource: getIt());
|
||||
|
||||
final SessionRepository _sessionRepository =
|
||||
SessionRepositoryImpl(sessionDataSource: getIt());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => MultiProvider(
|
||||
repositoryProviders: [
|
||||
RepositoryProvider<LoggerRepository>.value(value: _loggerRepository),
|
||||
RepositoryProvider<SessionRepository>.value(
|
||||
value: _sessionRepository,
|
||||
),
|
||||
RepositoryProvider<CryptoRepository>(
|
||||
create: (_) => CryptoRepositoryImpl(
|
||||
cryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
blocProviders: [
|
||||
BlocProvider<OutputCubit>(
|
||||
create: (_) => OutputCubit(_loggerRepository),
|
||||
),
|
||||
BlocProvider<ModeSwitcherCubit>(
|
||||
create: (_) => ModeSwitcherCubit(_sessionRepository),
|
||||
)
|
||||
],
|
||||
child: MaterialApp(
|
||||
title: 'NativeCrypto',
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: HomeStateManagement(),
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
// 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/native_crypto.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 'aes_state.dart';
|
||||
|
||||
class AESCubit extends Cubit<AESState> {
|
||||
AESCubit({
|
||||
required this.sessionRepository,
|
||||
required this.loggerRepository,
|
||||
required this.cryptoRepository,
|
||||
}) : super(const AESState.initial());
|
||||
final SessionRepository sessionRepository;
|
||||
final LoggerRepository loggerRepository;
|
||||
final CryptoRepository cryptoRepository;
|
||||
|
||||
FutureOr<void> encrypt(String message) 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,
|
||||
plainText: message.toBytes(),
|
||||
error: sk.err?.message,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await cryptoRepository.encrypt(message.toBytes(), sk.ok!);
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(cipherText) async {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('String successfully encrypted.\n'
|
||||
'Length: ${cipherText.length} bytes.\n'
|
||||
'Hex:\n${cipherText.toStr(to: Encoding.base16)}'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.success,
|
||||
plainText: message.toBytes(),
|
||||
cipherText: cipherText,
|
||||
plainTextFile: '',
|
||||
cipherTextFile: '',
|
||||
);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during encryption.'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.failure,
|
||||
plainText: message.toBytes(),
|
||||
error: error.message,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> alterMemory() async {
|
||||
emit(state.copyWith(state: State.loading));
|
||||
|
||||
if (state.cipherText?.isEmpty ?? true) {
|
||||
const error = 'Encrypt before decrypting!';
|
||||
await loggerRepository.addLog(
|
||||
const LogError(error),
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
state: State.failure,
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final altered = state.cipherText!;
|
||||
altered[0] += 1;
|
||||
|
||||
await loggerRepository.addLog(
|
||||
const LogWarning('In memory cipher text altered.'),
|
||||
);
|
||||
|
||||
emit(state.copyWith(cipherText: altered));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> decryptFromMemory() 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;
|
||||
}
|
||||
|
||||
if (state.cipherText?.isEmpty ?? true) {
|
||||
const error = 'Encrypt before decrypting!';
|
||||
await loggerRepository.addLog(
|
||||
const LogError(error),
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
state: State.failure,
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await cryptoRepository.decrypt(state.cipherText!, sk.ok!);
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(plainText) async {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('String successfully decrypted.\n'
|
||||
'Text:\n${plainText.toStr()}'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.success,
|
||||
plainText: plainText,
|
||||
plainTextFile: '',
|
||||
cipherTextFile: '',
|
||||
);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during decryption.'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.failure,
|
||||
error: error.message,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> decryptFromBase16(String message) async {
|
||||
emit(state.copyWith(state: State.loading));
|
||||
|
||||
final sk = await sessionRepository.getSessionKey();
|
||||
final cipherText = message.toBytes(from: Encoding.base16);
|
||||
|
||||
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,
|
||||
cipherText: cipherText,
|
||||
error: sk.err?.message,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await cryptoRepository.decrypt(cipherText, sk.ok!);
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(plainText) async {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('String successfully decrypted.\n'
|
||||
'Text:\n${plainText.toStr()}'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.success,
|
||||
plainText: plainText,
|
||||
cipherText: cipherText,
|
||||
plainTextFile: '',
|
||||
cipherTextFile: '',
|
||||
);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during decryption.'),
|
||||
);
|
||||
return state.copyWith(
|
||||
state: State.failure,
|
||||
cipherText: cipherText,
|
||||
error: error.message,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
// 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 'aes_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class AESState {
|
||||
const AESState(
|
||||
this.state,
|
||||
this.plainText,
|
||||
this.cipherText,
|
||||
this.plainTextFile,
|
||||
this.cipherTextFile,
|
||||
this.error,
|
||||
);
|
||||
|
||||
const AESState.initial()
|
||||
: state = State.initial,
|
||||
plainText = null,
|
||||
plainTextFile = null,
|
||||
cipherText = null,
|
||||
cipherTextFile = null,
|
||||
error = null;
|
||||
|
||||
final State state;
|
||||
final Uint8List? plainText;
|
||||
final Uint8List? cipherText;
|
||||
final String? plainTextFile;
|
||||
final String? cipherTextFile;
|
||||
final String? error;
|
||||
|
||||
AESState copyWith({
|
||||
State? state,
|
||||
Uint8List? plainText,
|
||||
Uint8List? cipherText,
|
||||
String? plainTextFile,
|
||||
String? cipherTextFile,
|
||||
String? error,
|
||||
}) =>
|
||||
AESState(
|
||||
state ?? this.state,
|
||||
plainText ?? this.plainText,
|
||||
cipherText ?? this.cipherText,
|
||||
plainTextFile ?? this.plainTextFile,
|
||||
cipherTextFile ?? this.cipherTextFile,
|
||||
error ?? this.error,
|
||||
);
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
// 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/cipher/blocs/aes/aes_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 AESStateManagement extends CubitScreen<AESCubit, AESState> {
|
||||
AESStateManagement({super.key});
|
||||
|
||||
final TextEditingController _plainTextTextController = TextEditingController()
|
||||
..text = 'abc';
|
||||
|
||||
final TextEditingController _cipherTextTextController =
|
||||
TextEditingController();
|
||||
|
||||
@override
|
||||
AESCubit create(BuildContext context) => AESCubit(
|
||||
sessionRepository: repo<SessionRepository>(context),
|
||||
loggerRepository: repo<LoggerRepository>(context),
|
||||
cryptoRepository: repo<CryptoRepository>(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, AESState state) => ListView(
|
||||
children: [
|
||||
const Logs(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'AES256-GCM',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'''AES is a symmetric-key encryption algorithm that is widely used to encrypt sensitive data. It works by encrypting plaintext (the original data) using a secret key and a set of predefined mathematical operations (known as a cipher). The result is ciphertext (the encrypted data) that can only be decrypted and read by someone who has access to the secret key.\nGCM is a mode of operation for AES that provides both confidentiality and authenticity. It works by combining a block cipher (such as AES) with a Galois Field (a mathematical structure used to define a specific type of encryption) and a Counter (a value that is incremented for each block of data that is processed).''',
|
||||
style: AppTypography.body,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
controller: _plainTextTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'PlainText',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Encrypt',
|
||||
onPressed: () =>
|
||||
bloc(context).encrypt(_plainTextTextController.text),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Alter',
|
||||
onPressed: () => bloc(context).alterMemory(),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Decrypt',
|
||||
onPressed: () => bloc(context).decryptFromMemory(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'External CipherText',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
controller: _cipherTextTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Base16 CipherText',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Decrypt',
|
||||
onPressed: () => bloc(context)
|
||||
.decryptFromBase16(_cipherTextTextController.text),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// 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/native_crypto.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';
|
||||
|
||||
part 'hash_state.dart';
|
||||
|
||||
class HashCubit extends Cubit<HashState> {
|
||||
HashCubit({
|
||||
required this.loggerRepository,
|
||||
required this.cryptoRepository,
|
||||
}) : super(const HashState.initial());
|
||||
|
||||
final LoggerRepository loggerRepository;
|
||||
final CryptoRepository cryptoRepository;
|
||||
|
||||
FutureOr<void> hash(Hash hasher, String message) async {
|
||||
emit(const HashState.loading());
|
||||
final result = await cryptoRepository.hash(hasher, message.toBytes());
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(digest) async {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('Hash $message using ${hasher.algorithm.name}.\n'
|
||||
'Length: ${digest.length} bytes.\n'
|
||||
'Hex:\n${digest.toStr(to: Encoding.base16)}'),
|
||||
);
|
||||
return HashState.success(digest);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during digest.'),
|
||||
);
|
||||
return HashState.failure(error.message);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// 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 'hash_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class HashState {
|
||||
const HashState.initial()
|
||||
: state = State.initial,
|
||||
result = null,
|
||||
error = null;
|
||||
|
||||
const HashState.loading()
|
||||
: state = State.loading,
|
||||
result = null,
|
||||
error = null;
|
||||
|
||||
const HashState.failure(this.error)
|
||||
: state = State.failure,
|
||||
result = null;
|
||||
|
||||
const HashState.success(this.result)
|
||||
: state = State.success,
|
||||
error = null;
|
||||
|
||||
final State state;
|
||||
final Uint8List? result;
|
||||
final String? error;
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
// 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/native_crypto.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/presentation/hash/blocs/hash/hash_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 HashStateManagement extends CubitScreen<HashCubit, HashState> {
|
||||
HashStateManagement({super.key});
|
||||
|
||||
final TextEditingController _hashTextController = TextEditingController()
|
||||
..text = 'abc';
|
||||
|
||||
@override
|
||||
HashCubit create(BuildContext context) => HashCubit(
|
||||
loggerRepository: repo<LoggerRepository>(context),
|
||||
cryptoRepository: repo<CryptoRepository>(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, HashState state) => ListView(
|
||||
children: [
|
||||
const Logs(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'Hash',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'''SHA-256, SHA-384, and SHA-512 are cryptographic hash functions. They are used to create a fixed-size output (known as a hash or digest) from an input of any size. The output of a hash function is deterministic, which means that the same input will always produce the same output.''',
|
||||
style: AppTypography.body,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
controller: _hashTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Message',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'SHA256',
|
||||
onPressed: () => bloc(context).hash(
|
||||
Sha256(),
|
||||
_hashTextController.text,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'SHA384',
|
||||
onPressed: () => bloc(context).hash(
|
||||
Sha384(),
|
||||
_hashTextController.text,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'SHA512',
|
||||
onPressed: () => bloc(context).hash(
|
||||
Sha512(),
|
||||
_hashTextController.text,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// 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/mode.dart';
|
||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
||||
|
||||
part 'mode_switcher_state.dart';
|
||||
|
||||
class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
|
||||
ModeSwitcherCubit(
|
||||
this.sessionRepository,
|
||||
) : super(const ModeSwitcherState(NativeCryptoMode()));
|
||||
SessionRepository sessionRepository;
|
||||
|
||||
FutureOr<void> switchMode() async {
|
||||
final currentMode = await sessionRepository.getCurrentMode();
|
||||
Mode? newMode;
|
||||
|
||||
if (currentMode.isOk) {
|
||||
if (currentMode.ok == const NativeCryptoMode()) {
|
||||
newMode = const PointyCastleMode();
|
||||
} else {
|
||||
newMode = const NativeCryptoMode();
|
||||
}
|
||||
|
||||
sessionRepository.setCurrentMode(newMode);
|
||||
} else {
|
||||
newMode = const NativeCryptoMode();
|
||||
sessionRepository.setCurrentMode(newMode);
|
||||
}
|
||||
|
||||
emit(ModeSwitcherState(newMode));
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// 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 'mode_switcher_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class ModeSwitcherState {
|
||||
const ModeSwitcherState(this.currentMode);
|
||||
|
||||
final Mode currentMode;
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ModeSwitcherState other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.currentMode == currentMode;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => currentMode.hashCode;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
// 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';
|
||||
|
||||
part 'navigation_bar_state.dart';
|
||||
|
||||
class NavigationBarCubit extends Cubit<NavigationBarState> {
|
||||
NavigationBarCubit() : super(const NavigationBarState(0));
|
||||
|
||||
FutureOr<void> changePage(int page) {
|
||||
emit(NavigationBarState(page));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// 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 'navigation_bar_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class NavigationBarState {
|
||||
const NavigationBarState(this.index);
|
||||
|
||||
final int index;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
// 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/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';
|
||||
|
||||
class HomeStateManagement
|
||||
extends CubitScreen<NavigationBarCubit, NavigationBarState> {
|
||||
HomeStateManagement({super.key});
|
||||
|
||||
final List<Widget> _children = [
|
||||
KeyDerivationStateManagement(),
|
||||
HashStateManagement(),
|
||||
AESStateManagement(),
|
||||
TestVectorsStateManagement(),
|
||||
const Blank()
|
||||
];
|
||||
|
||||
@override
|
||||
NavigationBarCubit create(BuildContext context) => NavigationBarCubit();
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, NavigationBarState state) => Scaffold(
|
||||
appBar: const PreferredSize(
|
||||
preferredSize: Size(double.infinity, 60),
|
||||
child: AppBarStateManagement(),
|
||||
),
|
||||
body: _children[state.index],
|
||||
bottomNavigationBar: BottomNavigationBarStateManagement(
|
||||
onTap: (page) => bloc(context).changePage(page), // new
|
||||
currentIndex: state.index, // new
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.vpn_key),
|
||||
label: 'Key',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.tag),
|
||||
label: 'Hash',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.lock),
|
||||
label: 'Encryption',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.rule_rounded),
|
||||
label: 'Tests',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.timer),
|
||||
label: 'Benchmark',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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/domain/entities/mode.dart';
|
||||
import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class AppBarStateManagement
|
||||
extends CubitConsumerScreen<ModeSwitcherCubit, ModeSwitcherState> {
|
||||
const AppBarStateManagement({super.key});
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, ModeSwitcherState state) => AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
state.currentMode == const NativeCryptoMode()
|
||||
? 'NativeCrypto'
|
||||
: 'PointyCastle',
|
||||
),
|
||||
backgroundColor: state.currentMode.primaryColor,
|
||||
// TODO(hpcl): enable mode switcher
|
||||
// actions: [
|
||||
// Switch(
|
||||
// value: state.currentMode == const NativeCryptoMode(),
|
||||
// onChanged: (_) => bloc(context).switchMode(),
|
||||
// )
|
||||
// ],
|
||||
);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// 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/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class BottomNavigationBarStateManagement
|
||||
extends CubitConsumerScreen<ModeSwitcherCubit, ModeSwitcherState> {
|
||||
const BottomNavigationBarStateManagement({
|
||||
required this.items,
|
||||
this.currentIndex = 0,
|
||||
this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final void Function(int)? onTap;
|
||||
final int currentIndex;
|
||||
final List<BottomNavigationBarItem> items;
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, ModeSwitcherState state) =>
|
||||
BottomNavigationBar(
|
||||
selectedItemColor: state.currentMode.primaryColor,
|
||||
unselectedItemColor: Colors.black,
|
||||
showUnselectedLabels: true,
|
||||
onTap: onTap,
|
||||
currentIndex: currentIndex,
|
||||
items: items,
|
||||
type: BottomNavigationBarType.shifting,
|
||||
);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// 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:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:native_crypto_example/core/typography.dart';
|
||||
import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class ButtonStateManagement
|
||||
extends CubitConsumerScreen<ModeSwitcherCubit, ModeSwitcherState> {
|
||||
const ButtonStateManagement({
|
||||
required this.label,
|
||||
this.onPressed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final void Function()? onPressed;
|
||||
final String label;
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, ModeSwitcherState state) {
|
||||
context.watch<ModeSwitcherCubit>();
|
||||
return ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: state.currentMode.primaryColor,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: AppTypography.body,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// 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';
|
||||
|
||||
class Blank extends StatelessWidget {
|
||||
const Blank({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
const Center(child: Text('Nothing to show'));
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
// 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 'key_derivation_state.dart';
|
||||
|
||||
class KeyDerivationCubit extends Cubit<KeyDerivationState> {
|
||||
KeyDerivationCubit({
|
||||
required this.sessionRepository,
|
||||
required this.loggerRepository,
|
||||
required this.cryptoRepository,
|
||||
}) : super(const KeyDerivationState.initial());
|
||||
final SessionRepository sessionRepository;
|
||||
final LoggerRepository loggerRepository;
|
||||
final CryptoRepository cryptoRepository;
|
||||
|
||||
FutureOr<void> generate() async {
|
||||
emit(const KeyDerivationState.loading());
|
||||
final result = await cryptoRepository.generateSecureRandom(32);
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(key) async {
|
||||
await sessionRepository.setSessionKey(key);
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('SecretKey successfully generated.\n'
|
||||
'Length: ${key.bytes.length} bytes.\n'
|
||||
'Hex:\n${key.base16}'),
|
||||
);
|
||||
return KeyDerivationState.success(key.bytes);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during key generation.'),
|
||||
);
|
||||
return KeyDerivationState.failure(error.message);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> pbkdf2(String password) async {
|
||||
emit(const KeyDerivationState.loading());
|
||||
if (password.isEmpty) {
|
||||
const error = 'Password is empty';
|
||||
await loggerRepository.addLog(
|
||||
const LogError(error),
|
||||
);
|
||||
emit(const KeyDerivationState.failure(error));
|
||||
}
|
||||
final result =
|
||||
await cryptoRepository.deriveKeyFromPassword(password, salt: 'salt');
|
||||
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(key) async {
|
||||
await sessionRepository.setSessionKey(key);
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('SecretKey successfully derivated from $password.\n'
|
||||
'Length: ${key.bytes.length} bytes.\n'
|
||||
'Hex:\n${key.base16}'),
|
||||
);
|
||||
return KeyDerivationState.success(key.bytes);
|
||||
},
|
||||
(error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during key derivation.'),
|
||||
);
|
||||
return KeyDerivationState.failure(error.message);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> getSessionKey() async {
|
||||
emit(const KeyDerivationState.loading());
|
||||
final sk = await sessionRepository.getSessionKey();
|
||||
emit(
|
||||
await sk.foldAsync((key) async {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('Session key successfully retreived.\n'
|
||||
'Length: ${key.bytes.length} bytes.\n'
|
||||
'Hex:\n${key.base16}'),
|
||||
);
|
||||
return KeyDerivationState.success(key.bytes);
|
||||
}, (error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(error.message ?? 'Error during key retrieving.'),
|
||||
);
|
||||
return KeyDerivationState.failure(error.message);
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// 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 'key_derivation_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class KeyDerivationState {
|
||||
const KeyDerivationState.initial()
|
||||
: state = State.initial,
|
||||
result = null,
|
||||
error = null;
|
||||
|
||||
const KeyDerivationState.loading()
|
||||
: state = State.loading,
|
||||
result = null,
|
||||
error = null;
|
||||
|
||||
const KeyDerivationState.failure(this.error)
|
||||
: state = State.failure,
|
||||
result = null;
|
||||
|
||||
const KeyDerivationState.success(this.result)
|
||||
: state = State.success,
|
||||
error = null;
|
||||
|
||||
final State state;
|
||||
final Uint8List? result;
|
||||
final String? error;
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
// 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/home/state_management/widgets/button_state_management.dart';
|
||||
import 'package:native_crypto_example/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart';
|
||||
import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class KeyDerivationStateManagement
|
||||
extends CubitScreen<KeyDerivationCubit, KeyDerivationState> {
|
||||
KeyDerivationStateManagement({super.key});
|
||||
|
||||
final TextEditingController _pwdTextController = TextEditingController()
|
||||
..text = 'password';
|
||||
|
||||
@override
|
||||
KeyDerivationCubit create(BuildContext context) => KeyDerivationCubit(
|
||||
sessionRepository: repo<SessionRepository>(context),
|
||||
loggerRepository: repo<LoggerRepository>(context),
|
||||
cryptoRepository: repo<CryptoRepository>(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, KeyDerivationState state) => ListView(
|
||||
children: [
|
||||
const Logs(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'Random secret key',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'''A secret key is a piece of information that is used to encrypt and decrypt messages. In symmetric-key encryption, the same secret key is used for both encrypting the original data and for decrypting the encrypted data. The key is kept secret so that only authorized parties can read the encrypted messages.''',
|
||||
style: AppTypography.body,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Generate secret key',
|
||||
onPressed: () => bloc(context).generate(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'PBKDF2',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'''PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function that is designed to be computationally expensive and memory-intensive, in order to slow down the process of guessing a password. This makes it more difficult for an attacker to guess a password by using a brute-force attack, which involves trying every possible combination of characters until the correct password is found.''',
|
||||
style: AppTypography.body,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
controller: _pwdTextController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Password',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Compute PBKDF2',
|
||||
onPressed: () => bloc(context).pbkdf2(_pwdTextController.text),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'Session',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Retrieve session key',
|
||||
onPressed: () => bloc(context).getSessionKey(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// 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/repositories/logger_repository.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
part 'output_state.dart';
|
||||
|
||||
class OutputCubit extends Cubit<OutputState> {
|
||||
OutputCubit(this.loggerRepository) : super(const OutputState.empty()) {
|
||||
logSubscription = loggerRepository.streamLogs().listen((message) {
|
||||
if (message.isOk) {
|
||||
onMessage(message.ok!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final LoggerRepository loggerRepository;
|
||||
late StreamSubscription<Result<Map<DateTime, LogMessage>, AppException>>
|
||||
logSubscription;
|
||||
|
||||
FutureOr<void> add(LogMessage message) {
|
||||
loggerRepository.addLog(message);
|
||||
}
|
||||
|
||||
FutureOr<void> clear() {
|
||||
loggerRepository.clearLog();
|
||||
emit(const OutputState.empty());
|
||||
}
|
||||
|
||||
FutureOr<void> onMessage(Map<DateTime, LogMessage> entries) {
|
||||
emit(OutputState(entries: entries));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
logSubscription.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// 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 'output_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class OutputState {
|
||||
const OutputState({
|
||||
required this.entries,
|
||||
});
|
||||
|
||||
const OutputState.empty() : entries = const {};
|
||||
|
||||
final Map<DateTime, LogMessage> entries;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
entries.forEach((key, value) {
|
||||
buffer
|
||||
..write(value.prefix)
|
||||
..write(' at ')
|
||||
..write(key.toIso8601String())
|
||||
..write(':\t')
|
||||
..writeln(value.message);
|
||||
});
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
// 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/presentation/output/blocs/output/output_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class OutputStateManagement
|
||||
extends CubitConsumerScreen<OutputCubit, OutputState> {
|
||||
OutputStateManagement({super.key});
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, OutputState state) {
|
||||
// Auto scroll
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_scrollController.hasClients) {
|
||||
_scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return SizedBox(
|
||||
height: 250,
|
||||
child: ListView.separated(
|
||||
controller: _scrollController,
|
||||
itemCount: state.entries.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: SizedBox(
|
||||
height: 1,
|
||||
child: ColoredBox(color: Colors.black),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, index) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
state.entries.values.elementAt(index).prefix,
|
||||
style: TextStyle(
|
||||
color: state.entries.values.elementAt(index).color,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
state.entries.keys.elementAt(index).toIso8601String(),
|
||||
style: TextStyle(
|
||||
color: state.entries.values.elementAt(index).color,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SelectableText(
|
||||
state.entries.values.elementAt(index).message,
|
||||
style: TextStyle(
|
||||
color: state.entries.values.elementAt(index).color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// 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/presentation/home/state_management/widgets/button_state_management.dart';
|
||||
import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class ClearButtonStateManagement
|
||||
extends CubitConsumerScreen<OutputCubit, OutputState> {
|
||||
const ClearButtonStateManagement({super.key});
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, OutputState state) =>
|
||||
ButtonStateManagement(
|
||||
label: 'Clear',
|
||||
onPressed: () => bloc(context).clear(),
|
||||
);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// 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/presentation/output/state_management/output_state_management.dart';
|
||||
import 'package:native_crypto_example/presentation/output/state_management/widgets/clear_button_state_management.dart';
|
||||
|
||||
class Logs extends StatelessWidget {
|
||||
const Logs({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: const [
|
||||
Text(
|
||||
'Logs',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
ClearButtonStateManagement(),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
width: double.infinity,
|
||||
child: ColoredBox(color: Colors.black),
|
||||
),
|
||||
OutputStateManagement(),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
width: double.infinity,
|
||||
child: ColoredBox(color: Colors.black),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
// 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/native_crypto.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/entities/test_vector.dart';
|
||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
|
||||
|
||||
part 'test_vectors_state.dart';
|
||||
|
||||
class TestVectorsCubit extends Cubit<TestVectorsState> {
|
||||
TestVectorsCubit({
|
||||
required this.loggerRepository,
|
||||
required this.cryptoRepository,
|
||||
}) : super(const TestVectorsState.initial());
|
||||
final LoggerRepository loggerRepository;
|
||||
final CryptoRepository cryptoRepository;
|
||||
|
||||
final tests = [
|
||||
TestVector(
|
||||
key: 'b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4',
|
||||
nonce: '516c33929df5a3284ff463d7',
|
||||
plainText: '',
|
||||
cipherText: '',
|
||||
tag: 'bdc1ac884d332457a1d2664f168c76f0',
|
||||
),
|
||||
TestVector(
|
||||
key: '5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f',
|
||||
nonce: '770ac1a5a3d476d5d96944a1',
|
||||
plainText: '',
|
||||
cipherText: '',
|
||||
tag: '196d691e1047093ca4b3d2ef4baba216',
|
||||
),
|
||||
TestVector(
|
||||
key: '7620b79b17b21b06d97019aa70e1ca105e1c03d2a0cf8b20b5a0ce5c3903e548',
|
||||
nonce: '60f56eb7a4b38d4f03395511',
|
||||
plainText: '',
|
||||
cipherText: '',
|
||||
tag: 'f570c38202d94564bab39f75617bc87a',
|
||||
),
|
||||
TestVector(
|
||||
key: '31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22',
|
||||
nonce: '0d18e06c7c725ac9e362e1ce',
|
||||
plainText: '2db5168e932556f8089a0622981d017d',
|
||||
cipherText: 'fa4362189661d163fcd6a56d8bf0405a',
|
||||
tag: 'd636ac1bbedd5cc3ee727dc2ab4a9489',
|
||||
),
|
||||
TestVector(
|
||||
key: '460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99',
|
||||
nonce: '8a4a16b9e210eb68bcb6f58d',
|
||||
plainText: '99e4e926ffe927f691893fb79a96b067',
|
||||
cipherText: '133fc15751621b5f325c7ff71ce08324',
|
||||
tag: 'ec4e87e0cf74a13618d0b68636ba9fa7',
|
||||
),
|
||||
TestVector(
|
||||
key: 'f78a2ba3c5bd164de134a030ca09e99463ea7e967b92c4b0a0870796480297e5',
|
||||
nonce: '2bb92fcb726c278a2fa35a88',
|
||||
plainText: 'f562509ed139a6bbe7ab545ac616250c',
|
||||
cipherText: 'e2f787996e37d3b47294bf7ebba5ee25',
|
||||
tag: '00f613eee9bdad6c9ee7765db1cb45c0',
|
||||
),
|
||||
];
|
||||
|
||||
FutureOr<void> launchAllTests() async {
|
||||
emit(const TestVectorsState.loading());
|
||||
int i = 0;
|
||||
for (final test in tests) {
|
||||
final sk = SecretKey(test.keyBytes);
|
||||
final iv = test.nonceBytes;
|
||||
|
||||
// Encryption
|
||||
final encryption =
|
||||
await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv);
|
||||
final testCipherText = await encryption
|
||||
.foldAsync((cipherText) async => cipherText, (error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(
|
||||
'TEST $i :: ${error.message ?? 'Error during encryption.'} ❌',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
final encryptionSuccess = test.validateCipherText(testCipherText);
|
||||
|
||||
if (encryptionSuccess) {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('TEST $i :: Encryption test passed ✅'),
|
||||
);
|
||||
} else {
|
||||
await loggerRepository.addLog(
|
||||
LogError('TEST $i :: Encryption test failed ❌'),
|
||||
);
|
||||
}
|
||||
|
||||
// Decryption
|
||||
final decryption =
|
||||
await cryptoRepository.decrypt(test.cipherTextBytes, sk);
|
||||
final testPlainText = await decryption
|
||||
.foldAsync((plainText) async => plainText, (error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(
|
||||
'TEST $i :: ${error.message ?? 'Error during decryption. ❌'}',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
final decryptionSuccess = test.validatePlainText(testPlainText);
|
||||
|
||||
if (decryptionSuccess) {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('TEST $i :: Decryption test passed ✅'),
|
||||
);
|
||||
} else {
|
||||
await loggerRepository.addLog(
|
||||
LogError('TEST $i :: Decryption test failed ❌'),
|
||||
);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == tests.length) {
|
||||
emit(const TestVectorsState.success());
|
||||
} else {
|
||||
emit(const TestVectorsState.failure('Error during the test suite'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FutureOr<void> launchTest(String number) async {
|
||||
try {
|
||||
final id = int.parse(number);
|
||||
|
||||
final test = tests.elementAt(id);
|
||||
|
||||
final sk = SecretKey(test.keyBytes);
|
||||
final iv = test.nonceBytes;
|
||||
|
||||
// Encryption
|
||||
final encryption =
|
||||
await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv);
|
||||
final testCipherText = await encryption
|
||||
.foldAsync((cipherText) async => cipherText, (error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(
|
||||
'TEST $id :: ${error.message ?? 'Error during encryption.'} ❌',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
final encryptionSuccess = test.validateCipherText(testCipherText);
|
||||
|
||||
if (encryptionSuccess) {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('TEST $id :: Encryption test passed ✅'),
|
||||
);
|
||||
} else {
|
||||
await loggerRepository.addLog(
|
||||
LogError('TEST $id :: Encryption test failed ❌'),
|
||||
);
|
||||
}
|
||||
|
||||
// Decryption
|
||||
final decryption =
|
||||
await cryptoRepository.decrypt(test.cipherTextBytes, sk);
|
||||
final testPlainText = await decryption
|
||||
.foldAsync((plainText) async => plainText, (error) async {
|
||||
await loggerRepository.addLog(
|
||||
LogError(
|
||||
'TEST $id :: ${error.message ?? 'Error during decryption. ❌'}',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
final decryptionSuccess = test.validatePlainText(testPlainText);
|
||||
|
||||
if (decryptionSuccess) {
|
||||
await loggerRepository.addLog(
|
||||
LogInfo('TEST $id :: Decryption test passed ✅'),
|
||||
);
|
||||
} else {
|
||||
await loggerRepository.addLog(
|
||||
LogError('TEST $id :: Decryption test failed ❌'),
|
||||
);
|
||||
}
|
||||
emit(const TestVectorsState.success());
|
||||
} catch (e) {
|
||||
final error = 'Invalid vector number: $number';
|
||||
await loggerRepository.addLog(LogError(error));
|
||||
emit(TestVectorsState.failure(error));
|
||||
}
|
||||
|
||||
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 'test_vectors_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class TestVectorsState {
|
||||
const TestVectorsState.initial()
|
||||
: state = State.initial,
|
||||
error = null;
|
||||
|
||||
const TestVectorsState.loading()
|
||||
: state = State.loading,
|
||||
error = null;
|
||||
|
||||
const TestVectorsState.failure(this.error) : state = State.failure;
|
||||
|
||||
const TestVectorsState.success()
|
||||
: state = State.success,
|
||||
error = null;
|
||||
|
||||
final State state;
|
||||
final String? error;
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
// 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/presentation/home/state_management/widgets/button_state_management.dart';
|
||||
import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
|
||||
import 'package:native_crypto_example/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart';
|
||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||
|
||||
class TestVectorsStateManagement
|
||||
extends CubitScreen<TestVectorsCubit, TestVectorsState> {
|
||||
TestVectorsStateManagement({super.key});
|
||||
|
||||
final TextEditingController _vectorNumberTextController =
|
||||
TextEditingController();
|
||||
|
||||
@override
|
||||
TestVectorsCubit create(BuildContext context) => TestVectorsCubit(
|
||||
loggerRepository: repo<LoggerRepository>(context),
|
||||
cryptoRepository: repo<CryptoRepository>(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget onBuild(BuildContext context, TestVectorsState state) => ListView(
|
||||
children: [
|
||||
const Logs(),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'Test vectors',
|
||||
style: AppTypography.title,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(
|
||||
'''In cryptography, a test vector is a set of input values that is used to test the correct implementation and operation of a cryptographic algorithm or system. Test vectors are usually provided by the creators of the algorithm, or by national standardization organizations, and can be used to verify that an implementation of the algorithm is correct and produces the expected output for a given set of inputs.''',
|
||||
style: AppTypography.body,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'All Tests',
|
||||
onPressed: () => bloc(context).launchAllTests(),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
controller: _vectorNumberTextController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Vector number',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ButtonStateManagement(
|
||||
label: 'Specific Test',
|
||||
onPressed: () =>
|
||||
bloc(context).launchTest(_vectorNumberTextController.text),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: session.dart
|
||||
// Created Date: 28/12/2021 13:54:29
|
||||
// Last Modified: 28/12/2021 13:58:49
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:native_crypto/native_crypto.dart';
|
||||
|
||||
class Session {
|
||||
SecretKey secretKey;
|
||||
Session() : secretKey = SecretKey(Uint8List(0));
|
||||
|
||||
void setKey(SecretKey sk) {
|
||||
secretKey = sk;
|
||||
}
|
||||
}
|
||||
|
||||
// Providers
|
||||
|
||||
final sessionProvider = StateProvider<Session>((ref) => Session());
|
@ -1,31 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: button.dart
|
||||
// Created Date: 28/12/2021 13:31:17
|
||||
// Last Modified: 28/12/2021 13:31:34
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Button extends StatelessWidget {
|
||||
final void Function() onPressed;
|
||||
final String label;
|
||||
|
||||
const Button(this.onPressed, this.label, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: TextButton.styleFrom(
|
||||
primary: Colors.blue,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// Author: Hugo Pointcheval
|
||||
// Email: git@pcl.ovh
|
||||
// -----
|
||||
// File: output.dart
|
||||
// Created Date: 28/12/2021 13:31:39
|
||||
// Last Modified: 25/05/2022 16:39:39
|
||||
// -----
|
||||
// Copyright (c) 2021
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class Output extends StatelessWidget {
|
||||
late TextEditingController controller;
|
||||
final bool large;
|
||||
final bool editable;
|
||||
|
||||
Output({
|
||||
Key? key,
|
||||
TextEditingController? controller,
|
||||
this.large = false,
|
||||
this.editable = false,
|
||||
}) : super(key: key) {
|
||||
this.controller = controller ?? TextEditingController();
|
||||
}
|
||||
|
||||
void print(String message) {
|
||||
debugPrint(message);
|
||||
controller.text = message;
|
||||
}
|
||||
|
||||
void append(String message) {
|
||||
debugPrint(message);
|
||||
controller.text += message;
|
||||
}
|
||||
|
||||
void appendln(String message) {
|
||||
debugPrint(message);
|
||||
controller.text += message + "\n";
|
||||
}
|
||||
|
||||
void clear() {
|
||||
controller.clear();
|
||||
}
|
||||
|
||||
String read() {
|
||||
return controller.text;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
enableInteractiveSelection: true,
|
||||
readOnly: editable ? false : true,
|
||||
minLines: large ? 3 : 1,
|
||||
maxLines: large ? 500 : 5,
|
||||
decoration: const InputDecoration(border: OutlineInputBorder()),
|
||||
controller: controller,
|
||||
);
|
||||
}
|
||||
}
|
@ -3,10 +3,10 @@ description: Demonstrates how to use the native_crypto plugin.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
environment:
|
||||
sdk: ">=2.15.0 <3.0.0"
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
@ -15,40 +15,36 @@ environment:
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter: { sdk: flutter }
|
||||
native_crypto: { path: ../ }
|
||||
cupertino_icons: ^1.0.5
|
||||
flutter_riverpod: ^2.1.3
|
||||
pointycastle: ^3.6.2
|
||||
flutter_bloc: ^8.1.1
|
||||
|
||||
native_crypto:
|
||||
# When depending on this package from a real application you should use:
|
||||
# native_crypto: ^x.y.z
|
||||
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ../
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter_riverpod: ^1.0.3
|
||||
pointycastle: ^3.6.0
|
||||
wyatt_architecture:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.1.0+1
|
||||
wyatt_type_utils:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 0.0.4
|
||||
wyatt_bloc_helper:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 2.0.0
|
||||
get_it: ^7.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_test: { sdk: flutter }
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^2.0.0
|
||||
wyatt_analysis:
|
||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||
version: 2.4.1
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
|
12
packages/native_crypto/example/pubspec_overrides.yaml
Normal file
12
packages/native_crypto/example/pubspec_overrides.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
# melos_managed_dependency_overrides: native_crypto,native_crypto_android,native_crypto_ios,native_crypto_platform_interface,native_crypto_web
|
||||
dependency_overrides:
|
||||
native_crypto:
|
||||
path: ..
|
||||
native_crypto_android:
|
||||
path: ../../native_crypto_android
|
||||
native_crypto_ios:
|
||||
path: ../../native_crypto_ios
|
||||
native_crypto_platform_interface:
|
||||
path: ../../native_crypto_platform_interface
|
||||
native_crypto_web:
|
||||
path: ../../native_crypto_web
|
BIN
packages/native_crypto/example/web/favicon.png
Normal file
BIN
packages/native_crypto/example/web/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 917 B |
BIN
packages/native_crypto/example/web/icons/Icon-192.png
Normal file
BIN
packages/native_crypto/example/web/icons/Icon-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
packages/native_crypto/example/web/icons/Icon-512.png
Normal file
BIN
packages/native_crypto/example/web/icons/Icon-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
packages/native_crypto/example/web/icons/Icon-maskable-192.png
Normal file
BIN
packages/native_crypto/example/web/icons/Icon-maskable-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
packages/native_crypto/example/web/icons/Icon-maskable-512.png
Normal file
BIN
packages/native_crypto/example/web/icons/Icon-maskable-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
58
packages/native_crypto/example/web/index.html
Normal file
58
packages/native_crypto/example/web/index.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="example">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>example</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<script>
|
||||
// The value below is injected by flutter build, do not touch.
|
||||
var serviceWorkerVersion = null;
|
||||
</script>
|
||||
<!-- This script adds the flutter initialization JS code -->
|
||||
<script src="flutter.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.addEventListener('load', function(ev) {
|
||||
// Download main.dart.js
|
||||
_flutter.loader.loadEntrypoint({
|
||||
serviceWorker: {
|
||||
serviceWorkerVersion: serviceWorkerVersion,
|
||||
}
|
||||
}).then(function(engineInitializer) {
|
||||
return engineInitializer.initializeEngine();
|
||||
}).then(function(appRunner) {
|
||||
return appRunner.runApp();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
35
packages/native_crypto/example/web/manifest.json
Normal file
35
packages/native_crypto/example/web/manifest.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "example",
|
||||
"short_name": "example",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
"theme_color": "#0175C2",
|
||||
"description": "A new Flutter project.",
|
||||
"orientation": "portrait-primary",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
1
packages/native_crypto/example/web/pkg
Symbolic link
1
packages/native_crypto/example/web/pkg
Symbolic link
@ -0,0 +1 @@
|
||||
../../../native-crypto-rust/pkg/
|
Loading…
x
Reference in New Issue
Block a user