Compare commits
6 Commits
108c394a25
...
c98b9947b4
Author | SHA1 | Date | |
---|---|---|---|
c98b9947b4 | |||
01832a3b03 | |||
7dc07c693a | |||
550fe8b73e | |||
7c8f7206f0 | |||
0bf72447a0 |
202
README.md
202
README.md
@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255, 255, 255)">
|
<img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255, 255, 255)">
|
||||||
<h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5>
|
<h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5>
|
||||||
@ -42,34 +35,127 @@ For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB**
|
|||||||
|
|
||||||
In short, NativeCrypto is incomparable with PointyCastle.
|
In short, NativeCrypto is incomparable with PointyCastle.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Hash functions
|
||||||
|
- SHA-256
|
||||||
|
- SHA-384
|
||||||
|
- SHA-512
|
||||||
|
* HMAC functions
|
||||||
|
- HMAC-SHA-256
|
||||||
|
- HMAC-SHA-384
|
||||||
|
- HMAC-SHA-512
|
||||||
|
* Secure random
|
||||||
|
* PBKDF2
|
||||||
|
* AES
|
||||||
|
- Uint8List encryption/decryption
|
||||||
|
- File encryption/decryption
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
// Message to encrypt
|
||||||
|
final Uint8List message = 'Hello World!'.toBytes();
|
||||||
|
|
||||||
|
// Ask user for a password
|
||||||
|
final String password = await getPassword();
|
||||||
|
|
||||||
|
// Initialize a PBKDF2 object
|
||||||
|
final Pbkdf2 pbkdf2 = Pbkdf2(
|
||||||
|
length: 32, // 32 bytes
|
||||||
|
iterations: 1000,
|
||||||
|
salt: 'salt'.toBytes(),
|
||||||
|
hashAlgorithm: HashAlgorithm.sha256,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Derive a secret key from the password
|
||||||
|
final SecretKey secretKey = await pbkdf2(password: password);
|
||||||
|
|
||||||
|
// Initialize an AES cipher
|
||||||
|
final AES cipher = AES(
|
||||||
|
key: secretKey,
|
||||||
|
mode: AESMode.gcm,
|
||||||
|
padding: AESPadding.none,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Encrypt the message
|
||||||
|
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
|
||||||
|
|
||||||
|
// Decrypt the message
|
||||||
|
final Uint8List decryptedMessage = await cipher.decrypt(cipherText);
|
||||||
|
|
||||||
|
// Verify and print the decrypted message
|
||||||
|
assert(listEquals(message, decryptedMessage));
|
||||||
|
|
||||||
|
print(decryptedMessage.toStr());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the [example](./native_crypto/example) for a complete example.
|
||||||
|
|
||||||
|
Please take a look a the compatibility table below to check if your target is supported.
|
||||||
|
|
||||||
|
> Note: This **Flutter** example must run on a real device or a simulator.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
#### Compatibility
|
||||||
|
|
||||||
First, check compatibility with your targets.
|
First, check compatibility with your targets.
|
||||||
|
|
||||||
| iOS | Android | MacOS | Linux | Windows | Web |
|
| iOS | Android | MacOS | Linux | Windows | Web |
|
||||||
| --- | ------- | ----- | ----- | ------- | --- |
|
| --- | ------- | ----- | ----- | ------- | --- |
|
||||||
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
|
||||||
|
> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md))
|
||||||
|
|
||||||
|
NativeCrypto ciphertexts are formatted as follow:
|
||||||
|
|
||||||
|
```
|
||||||
|
+------------------+--------------------+------------------+
|
||||||
|
| Nonce (12 bytes) | Cipher text (n-28) | Tag (16 bytes) |
|
||||||
|
+------------------+--------------------+------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
> Warning: If your data comes from another source, make sur to use the same format.
|
||||||
|
|
||||||
#### Hash
|
#### Hash
|
||||||
|
|
||||||
To digest a message, you can use the following function:
|
To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Uint8List hash = await HashAlgorithm.sha256.digest(message);
|
Hash hasher = Sha256();
|
||||||
|
Uint8List digest = await hasher.digest(message);
|
||||||
```
|
```
|
||||||
|
|
||||||
> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
|
> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
|
||||||
|
|
||||||
#### Keys
|
#### HMAC
|
||||||
|
|
||||||
You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random.
|
To generate a HMAC, you'll need to initialize a `Hmac` object. Then, you can generate a HMAC from a message and a secret key.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]));
|
Hmac hmac = HmacSha256();
|
||||||
|
Uint8List hmac = await hmac.digest(message, secretKey);
|
||||||
|
```
|
||||||
|
|
||||||
|
> In NativeCrypto, you can use the following HMAC functions: HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512
|
||||||
|
|
||||||
|
#### Keys
|
||||||
|
|
||||||
|
You can build a `SecretKey` from utf8, utf16, base64, base16 (hex) strings, int list or raw bytes. You can also generate a SecretKey from secure random.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
SecretKey secretKey = SecretKey(bytes); // bytes is a Uint8List
|
||||||
SecretKey secretKey = SecretKey.fromUtf8('secret');
|
SecretKey secretKey = SecretKey.fromUtf8('secret');
|
||||||
|
SecretKet secretKey = SecretKey.fromUtf16('secret');
|
||||||
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
|
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
|
||||||
SecretKey secretKey = SecretKey.fromBase16('63657274');
|
SecretKey secretKey = SecretKey.fromBase16('63657274');
|
||||||
SecretKey secretKey = await SecretKey.fromSecureRandom(256);
|
SecretKey secretKey = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]);
|
||||||
|
SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Key derivation
|
#### Key derivation
|
||||||
@ -79,20 +165,21 @@ You can derive a `SecretKey` using **PBKDF2**.
|
|||||||
First, you need to initialize a `Pbkdf2` object.
|
First, you need to initialize a `Pbkdf2` object.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Pbkdf2 pbkdf2 = Pbkdf2(
|
final Pbkdf2 pbkdf2 = Pbkdf2(
|
||||||
keyBytesCount: 32,
|
length: 32, // 32 bytes
|
||||||
iterations: 1000,
|
iterations: 1000,
|
||||||
algorithm: HashAlgorithm.sha512,
|
salt: salt.toBytes(),
|
||||||
|
hashAlgorithm: HashAlgorithm.sha256,
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, you can derive a `SecretKey` from a password and salt.
|
Then, you can derive a `SecretKey` from a password.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt');
|
SecretKey secretKey = await pbkdf2(password: password);
|
||||||
```
|
```
|
||||||
|
|
||||||
> In NativeCrypto, you can use the following key derivation function: PBKDF2
|
> Note: Pbkdf2 is a callable class. You can use it like a function.
|
||||||
|
|
||||||
#### Cipher
|
#### Cipher
|
||||||
|
|
||||||
@ -101,44 +188,79 @@ And now, you can use the `SecretKey` to encrypt/decrypt a message.
|
|||||||
First, you need to initialize a `Cipher` object.
|
First, you need to initialize a `Cipher` object.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
AES cipher = AES(secretKey);
|
final AES cipher = AES(
|
||||||
|
key: key,
|
||||||
|
mode: AESMode.gcm,
|
||||||
|
padding: AESPadding.none,
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, you can encrypt your message.
|
Then, you can encrypt your message.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
CipherTextWrapper wrapper = await cipher.encrypt(message);
|
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
|
||||||
|
|
||||||
CipherText cipherText = wrapper.unwrap<CipherText>();
|
|
||||||
// same as
|
|
||||||
CipherText cipherText = wrapper.single;
|
|
||||||
|
|
||||||
// or
|
|
||||||
|
|
||||||
List<CipherText> cipherTexts = wrapper.unwrap<List<CipherText>>();
|
|
||||||
// same as
|
|
||||||
List<CipherText> cipherTexts = wrapper.list;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List<CipherText>` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured.
|
After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` .
|
||||||
|
|
||||||
Uppon receiving encrypted message, you can decrypt it.
|
Uppon receiving encrypted message `receivedData` , you can decrypt it.
|
||||||
You have to reconstruct the wrapper before decrypting.
|
You have to reconstruct the ciphertext and the setup the chunk factory.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
CipherTextWrapper wrapper = CipherTextWrapper.fromBytes(
|
final CipherText<AESCipherChunk> receivedCipherText CipherText(
|
||||||
data,
|
receivedData,
|
||||||
ivLength: AESMode.gcm.ivLength,
|
chunkFactory: (bytes) => AESCipherChunk(
|
||||||
tagLength: AESMode.gcm.tagLength,
|
bytes,
|
||||||
);
|
ivLength: cipher.mode.ivLength,
|
||||||
|
tagLength: cipher.mode.tagLength,
|
||||||
|
),
|
||||||
|
),
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, you can decrypt your message.
|
Then, you can decrypt your message.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Uint8List message = await cipher.decrypt(wrapper);
|
Uint8List message = await cipher.decrypt(receivedCipherText);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Files
|
||||||
|
|
||||||
|
You can encrypt/decrypt files.
|
||||||
|
|
||||||
|
First, you need to initialize a `Cipher` object.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final AES cipher = AES(
|
||||||
|
key: key,
|
||||||
|
mode: AESMode.gcm,
|
||||||
|
padding: AESPadding.none,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you can encrypt your file.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
await cipher.encryptFile(plainText, cipherText);
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: `plainText` and `cipherText` are `File` objects.
|
||||||
|
|
||||||
|
You can decrypt your file.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
await cipher.decryptFile(cipherText, plainText);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Advanced
|
||||||
|
|
||||||
|
You can force the use of a specific IV. Please note that the IV must be unique for each encryption.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final CipherText<AESCipherChunk> cipherText = await cipher.encryptWithIV(message, iv);
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Android
|
### Android
|
||||||
|
@ -2,7 +2,7 @@ NativeCrypto
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 Hugo Pointcheval
|
Copyright (c) 2019 - 2023 Hugo Pointcheval
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,133 +1,3 @@
|
|||||||
<p align="center">
|
# NativeCrypto
|
||||||
<img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255,255,255)">
|
|
||||||
<h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
Readme available at [project root](../../README.md).
|
||||||
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
|
|
||||||
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://github.com/invertase/melos">
|
|
||||||
<img src="https://img.shields.io/badge/Maintained%20with-melos-f700ff.svg?style=flat-square" alt="Maintained with Melos" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://drone.wyatt-studio.fr/hugo/native-crypto">
|
|
||||||
<img src="https://drone.wyatt-studio.fr/api/badges/hugo/native-crypto/status.svg" alt="Build Status" />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[[Changelog]](./CHANGELOG.md) | [[License]](./LICENSE)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
The goal of this plugin is to provide a fast and powerful cryptographic functions by calling native libraries. On Android, it uses [javax.cypto](https://developer.android.com/reference/javax/crypto/package-summary), and on iOS, it uses [CommonCrypto](https://opensource.apple.com/source/CommonCrypto/) and [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
|
|
||||||
|
|
||||||
I started this projet because I wanted to add cryptographic functions on a Flutter app. But I faced a problem with the well-known [Pointy Castle](https://pub.dev/packages/pointycastle) library: the performance was very poor. Here some benchmarks and comparison:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB** in **~5.6s** with PointyCastle and in **~40ms** with NativeCrypto. And on an *OnePlus 5*, you can encrypt/decrypt a message of **50MiB** in **~6min30** with PointyCastle and in less than **~1s** with NativeCrypto.
|
|
||||||
|
|
||||||
In short, NativeCrypto is incomparable with PointyCastle.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
First, check compatibility with your targets.
|
|
||||||
|
|
||||||
| iOS | Android | MacOS | Linux | Windows | Web |
|
|
||||||
| --- | ------- | ----- | ----- | ------- | --- |
|
|
||||||
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
||||||
|
|
||||||
#### Hash
|
|
||||||
|
|
||||||
To digest a message, you can use the following function:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
Uint8List hash = await HashAlgorithm.sha256.digest(message);
|
|
||||||
```
|
|
||||||
|
|
||||||
> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
|
|
||||||
|
|
||||||
#### Keys
|
|
||||||
|
|
||||||
You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]));
|
|
||||||
SecretKey secretKey = SecretKey.fromUtf8('secret');
|
|
||||||
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
|
|
||||||
SecretKey secretKey = SecretKey.fromBase16('63657274');
|
|
||||||
SecretKey secretKey = await SecretKey.fromSecureRandom(256);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Key derivation
|
|
||||||
|
|
||||||
You can derive a `SecretKey` using **PBKDF2**.
|
|
||||||
|
|
||||||
First, you need to initialize a `Pbkdf2` object.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
Pbkdf2 pbkdf2 = Pbkdf2(
|
|
||||||
keyBytesCount: 32,
|
|
||||||
iterations: 1000,
|
|
||||||
algorithm: HashAlgorithm.sha512,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, you can derive a `SecretKey` from a password and salt.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt');
|
|
||||||
```
|
|
||||||
|
|
||||||
> In NativeCrypto, you can use the following key derivation function: PBKDF2
|
|
||||||
|
|
||||||
#### Cipher
|
|
||||||
|
|
||||||
And now, you can use the `SecretKey` to encrypt/decrypt a message.
|
|
||||||
|
|
||||||
First, you need to initialize a `Cipher` object.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
AES cipher = AES(secretKey);
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, you can encrypt your message.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
CipherTextWrapper wrapper = await cipher.encrypt(message);
|
|
||||||
|
|
||||||
CipherText cipherText = wrapper.unwrap<CipherText>();
|
|
||||||
// same as
|
|
||||||
CipherText cipherText = wrapper.single;
|
|
||||||
|
|
||||||
// or
|
|
||||||
|
|
||||||
List<CipherText> cipherTexts = wrapper.unwrap<List<CipherText>>();
|
|
||||||
// same as
|
|
||||||
List<CipherText> cipherTexts = wrapper.list;
|
|
||||||
```
|
|
||||||
|
|
||||||
After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List<CipherText>` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured.
|
|
||||||
|
|
||||||
Uppon receiving encrypted message, you can decrypt it.
|
|
||||||
You have to reconstruct the wrapper before decrypting.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
CipherTextWrapper wrapper = CipherTextWrapper.fromBytes(
|
|
||||||
data,
|
|
||||||
ivLength: AESMode.gcm.ivLength,
|
|
||||||
tagLength: AESMode.gcm.tagLength,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, you can decrypt your message.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
Uint8List message = await cipher.decrypt(wrapper);
|
|
||||||
```
|
|
||||||
|
@ -1,21 +1,74 @@
|
|||||||
PODS:
|
PODS:
|
||||||
|
- DKImagePickerController/Core (4.3.4):
|
||||||
|
- DKImagePickerController/ImageDataManager
|
||||||
|
- DKImagePickerController/Resource
|
||||||
|
- DKImagePickerController/ImageDataManager (4.3.4)
|
||||||
|
- DKImagePickerController/PhotoGallery (4.3.4):
|
||||||
|
- DKImagePickerController/Core
|
||||||
|
- DKPhotoGallery
|
||||||
|
- DKImagePickerController/Resource (4.3.4)
|
||||||
|
- DKPhotoGallery (0.0.17):
|
||||||
|
- DKPhotoGallery/Core (= 0.0.17)
|
||||||
|
- DKPhotoGallery/Model (= 0.0.17)
|
||||||
|
- DKPhotoGallery/Preview (= 0.0.17)
|
||||||
|
- DKPhotoGallery/Resource (= 0.0.17)
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Core (0.0.17):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Preview
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Model (0.0.17):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Preview (0.0.17):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Resource
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Resource (0.0.17):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- file_picker (0.0.1):
|
||||||
|
- DKImagePickerController/PhotoGallery
|
||||||
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- native_crypto_ios (0.0.1):
|
- native_crypto_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- SDWebImage (5.15.5):
|
||||||
|
- SDWebImage/Core (= 5.15.5)
|
||||||
|
- SDWebImage/Core (5.15.5)
|
||||||
|
- SwiftyGif (5.4.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`)
|
- native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- DKImagePickerController
|
||||||
|
- DKPhotoGallery
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
file_picker:
|
||||||
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
native_crypto_ios:
|
native_crypto_ios:
|
||||||
:path: ".symlinks/plugins/native_crypto_ios/ios"
|
:path: ".symlinks/plugins/native_crypto_ios/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||||
|
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||||
|
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
|
native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
|
||||||
|
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
|
||||||
|
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
|
||||||
|
|
||||||
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
||||||
|
|
||||||
|
@ -47,5 +47,9 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UISupportsDocumentBrowser</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
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/data_sources/crypto_data_source.dart';
|
||||||
@ -30,6 +32,29 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
|
|||||||
return plainText;
|
return plainText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) async {
|
||||||
|
final AES cipher = AES(
|
||||||
|
key: key,
|
||||||
|
mode: AESMode.gcm,
|
||||||
|
padding: AESPadding.none,
|
||||||
|
);
|
||||||
|
|
||||||
|
final plainText = File.fromUri(
|
||||||
|
Uri.parse(
|
||||||
|
'${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await cipher.decryptFile(
|
||||||
|
cipherText,
|
||||||
|
plainText,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SecretKey> deriveKeyFromPassword(
|
Future<SecretKey> deriveKeyFromPassword(
|
||||||
String password, {
|
String password, {
|
||||||
@ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
|
|||||||
return cipherText.bytes;
|
return cipherText.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) async {
|
||||||
|
final AES cipher = AES(
|
||||||
|
key: key,
|
||||||
|
mode: AESMode.gcm,
|
||||||
|
padding: AESPadding.none,
|
||||||
|
);
|
||||||
|
|
||||||
|
final cipherText = File.fromUri(
|
||||||
|
Uri.parse(
|
||||||
|
'${folderResult.path}/${plainText.path.split('/').last}.enc',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await cipher.encryptFile(plainText, cipherText);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SecretKey> generateSecureRandom(int length) async {
|
Future<SecretKey> generateSecureRandom(int length) async {
|
||||||
final SecretKey sk = await SecretKey.fromSecureRandom(length);
|
final SecretKey sk = await SecretKey.fromSecureRandom(length);
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
// ignore_for_file: implementation_imports
|
// ignore_for_file: implementation_imports
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
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/data_sources/crypto_data_source.dart';
|
||||||
@ -37,6 +39,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
|
|||||||
return paddedPlainText;
|
return paddedPlainText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) async {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SecretKey> deriveKeyFromPassword(
|
Future<SecretKey> deriveKeyFromPassword(
|
||||||
String password, {
|
String password, {
|
||||||
@ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> encryptWithIV(
|
Future<Uint8List> encryptWithIV(
|
||||||
Uint8List data, SecretKey key, Uint8List iv,) async {
|
Uint8List data,
|
||||||
|
SecretKey key,
|
||||||
|
Uint8List iv,
|
||||||
|
) async {
|
||||||
final gcm = GCMBlockCipher(AESEngine())
|
final gcm = GCMBlockCipher(AESEngine())
|
||||||
..init(
|
..init(
|
||||||
true,
|
true,
|
||||||
@ -144,6 +158,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) async {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SecretKey> generateSecureRandom(int length) async {
|
Future<SecretKey> generateSecureRandom(int length) async {
|
||||||
if (_secureRandom == null) {
|
if (_secureRandom == null) {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
@ -30,6 +31,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOrResult<SecretKey> deriveKeyFromPassword(
|
FutureOrResult<SecretKey> deriveKeyFromPassword(
|
||||||
String password, {
|
String password, {
|
||||||
@ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOrResult<Uint8List> encryptWithIV(
|
FutureOrResult<Uint8List> encryptWithIV(
|
||||||
Uint8List data, SecretKey key, Uint8List iv,) =>
|
Uint8List data,
|
||||||
|
SecretKey key,
|
||||||
|
Uint8List iv,
|
||||||
|
) =>
|
||||||
Result.tryCatchAsync(
|
Result.tryCatchAsync(
|
||||||
() async => cryptoDataSource.encryptWithIV(data, key, iv),
|
() async => cryptoDataSource.encryptWithIV(data, key, iv),
|
||||||
(error) {
|
(error) {
|
||||||
@ -97,6 +117,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.encryptFile(plainText, folderResult, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOrResult<SecretKey> generateSecureRandom(int length) =>
|
FutureOrResult<SecretKey> generateSecureRandom(int length) =>
|
||||||
Result.tryCatchAsync(
|
Result.tryCatchAsync(
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright 2019-2023 Hugo Pointcheval
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
|
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
|
||||||
|
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||||
|
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
class CryptoRepositorySwitchableImpl extends CryptoRepository {
|
||||||
|
CryptoRepositorySwitchableImpl({
|
||||||
|
required this.nativeCryptoDataSource,
|
||||||
|
required this.pointyCastleDataSource,
|
||||||
|
required this.currentMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
CryptoDataSource nativeCryptoDataSource;
|
||||||
|
CryptoDataSource pointyCastleDataSource;
|
||||||
|
Mode currentMode;
|
||||||
|
|
||||||
|
set mode(Mode mode) {
|
||||||
|
currentMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoDataSource get cryptoDataSource {
|
||||||
|
if (currentMode is NativeCryptoMode) {
|
||||||
|
return nativeCryptoDataSource;
|
||||||
|
} else if (currentMode is PointyCastleMode) {
|
||||||
|
return pointyCastleDataSource;
|
||||||
|
} else {
|
||||||
|
throw Exception('Unknown mode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.decrypt(data, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<SecretKey> deriveKeyFromPassword(
|
||||||
|
String password, {
|
||||||
|
required String salt,
|
||||||
|
}) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.deriveKeyFromPassword(
|
||||||
|
password,
|
||||||
|
salt: salt,
|
||||||
|
),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.hash(hasher, data),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.hmac(hmac, data, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.encrypt(data, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<Uint8List> encryptWithIV(
|
||||||
|
Uint8List data,
|
||||||
|
SecretKey key,
|
||||||
|
Uint8List iv,
|
||||||
|
) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.encryptWithIV(data, key, iv),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.encryptFile(plainText, folderResult, key),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOrResult<SecretKey> generateSecureRandom(int length) =>
|
||||||
|
Result.tryCatchAsync(
|
||||||
|
() async => cryptoDataSource.generateSecureRandom(length),
|
||||||
|
(error) {
|
||||||
|
if (error is NativeCryptoException) {
|
||||||
|
return ClientException('${error.message}');
|
||||||
|
}
|
||||||
|
return ClientException(error.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -4,6 +4,8 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||||
@ -15,12 +17,22 @@ abstract class CryptoDataSource extends BaseDataSource {
|
|||||||
required String salt,
|
required String salt,
|
||||||
});
|
});
|
||||||
Future<Uint8List> encrypt(Uint8List data, SecretKey key);
|
Future<Uint8List> encrypt(Uint8List data, SecretKey key);
|
||||||
|
Future<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
);
|
||||||
Future<Uint8List> encryptWithIV(
|
Future<Uint8List> encryptWithIV(
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
SecretKey key,
|
SecretKey key,
|
||||||
Uint8List iv,
|
Uint8List iv,
|
||||||
);
|
);
|
||||||
Future<Uint8List> decrypt(Uint8List data, SecretKey key);
|
Future<Uint8List> decrypt(Uint8List data, SecretKey key);
|
||||||
|
Future<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
);
|
||||||
Future<Uint8List> hash(Hash hasher, Uint8List data);
|
Future<Uint8List> hash(Hash hasher, Uint8List data);
|
||||||
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||||
@ -15,12 +17,22 @@ abstract class CryptoRepository extends BaseRepository {
|
|||||||
required String salt,
|
required String salt,
|
||||||
});
|
});
|
||||||
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key);
|
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key);
|
||||||
|
FutureOrResult<void> encryptFile(
|
||||||
|
File plainText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
);
|
||||||
FutureOrResult<Uint8List> encryptWithIV(
|
FutureOrResult<Uint8List> encryptWithIV(
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
SecretKey key,
|
SecretKey key,
|
||||||
Uint8List iv,
|
Uint8List iv,
|
||||||
);
|
);
|
||||||
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key);
|
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key);
|
||||||
|
FutureOrResult<void> decryptFile(
|
||||||
|
File cipherText,
|
||||||
|
Uri folderResult,
|
||||||
|
SecretKey key,
|
||||||
|
);
|
||||||
|
|
||||||
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data);
|
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data);
|
||||||
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
|
||||||
|
@ -8,9 +8,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:native_crypto_example/core/get_it.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/data_sources/native_crypto_data_source_impl.dart';
|
||||||
import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart';
|
import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
|
||||||
|
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
|
||||||
import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart';
|
import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart';
|
||||||
import 'package:native_crypto_example/data/repositories/session_repository_impl.dart';
|
import 'package:native_crypto_example/data/repositories/session_repository_impl.dart';
|
||||||
|
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||||
import 'package:native_crypto_example/domain/repositories/logger_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/domain/repositories/session_repository.dart';
|
||||||
@ -28,6 +30,12 @@ class App extends StatelessWidget {
|
|||||||
final SessionRepository _sessionRepository =
|
final SessionRepository _sessionRepository =
|
||||||
SessionRepositoryImpl(sessionDataSource: getIt());
|
SessionRepositoryImpl(sessionDataSource: getIt());
|
||||||
|
|
||||||
|
final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl(
|
||||||
|
nativeCryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
|
||||||
|
pointyCastleDataSource: getIt<PointyCastleDataSourceImpl>(),
|
||||||
|
currentMode: const NativeCryptoMode(),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => MultiProvider(
|
Widget build(BuildContext context) => MultiProvider(
|
||||||
repositoryProviders: [
|
repositoryProviders: [
|
||||||
@ -35,18 +43,17 @@ class App extends StatelessWidget {
|
|||||||
RepositoryProvider<SessionRepository>.value(
|
RepositoryProvider<SessionRepository>.value(
|
||||||
value: _sessionRepository,
|
value: _sessionRepository,
|
||||||
),
|
),
|
||||||
RepositoryProvider<CryptoRepository>(
|
RepositoryProvider<CryptoRepository>.value(value: _cryptoRepository),
|
||||||
create: (_) => CryptoRepositoryImpl(
|
|
||||||
cryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
blocProviders: [
|
blocProviders: [
|
||||||
BlocProvider<OutputCubit>(
|
BlocProvider<OutputCubit>(
|
||||||
create: (_) => OutputCubit(_loggerRepository),
|
create: (_) => OutputCubit(_loggerRepository),
|
||||||
),
|
),
|
||||||
BlocProvider<ModeSwitcherCubit>(
|
BlocProvider<ModeSwitcherCubit>(
|
||||||
create: (_) => ModeSwitcherCubit(_sessionRepository),
|
create: (_) => ModeSwitcherCubit(
|
||||||
|
_sessionRepository,
|
||||||
|
_cryptoRepository,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2019-2023 Hugo Pointcheval
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:native_crypto_example/domain/entities/log_message.dart';
|
||||||
|
import 'package:native_crypto_example/domain/entities/states.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
||||||
|
|
||||||
|
part 'benchmark_state.dart';
|
||||||
|
|
||||||
|
class BenchmarkCubit extends Cubit<BenchmarkState> {
|
||||||
|
BenchmarkCubit({
|
||||||
|
required this.sessionRepository,
|
||||||
|
required this.loggerRepository,
|
||||||
|
required this.cryptoRepository,
|
||||||
|
}) : super(const BenchmarkState.initial());
|
||||||
|
final SessionRepository sessionRepository;
|
||||||
|
final LoggerRepository loggerRepository;
|
||||||
|
final CryptoRepository cryptoRepository;
|
||||||
|
|
||||||
|
List<int> testedSizes = [
|
||||||
|
2097152,
|
||||||
|
6291456,
|
||||||
|
10485760,
|
||||||
|
14680064,
|
||||||
|
18874368,
|
||||||
|
23068672,
|
||||||
|
27262976,
|
||||||
|
31457280,
|
||||||
|
35651584,
|
||||||
|
39845888,
|
||||||
|
44040192,
|
||||||
|
48234496,
|
||||||
|
52428800,
|
||||||
|
];
|
||||||
|
|
||||||
|
FutureOr<void> launchBenchmark() async {
|
||||||
|
emit(const BenchmarkState.loading());
|
||||||
|
|
||||||
|
final sk = await sessionRepository.getSessionKey();
|
||||||
|
|
||||||
|
if (sk.isErr) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No SecretKey!\n'
|
||||||
|
'Go in Key tab and generate or derive one.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
BenchmarkState.failure(
|
||||||
|
sk.err?.message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run = 0;
|
||||||
|
final csv = StringBuffer(
|
||||||
|
'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n',
|
||||||
|
);
|
||||||
|
for (final size in testedSizes) {
|
||||||
|
run++;
|
||||||
|
final StringBuffer csvLine = StringBuffer();
|
||||||
|
final dummyBytes = Uint8List(size);
|
||||||
|
csvLine.write('$run;$size;');
|
||||||
|
|
||||||
|
// Encryption
|
||||||
|
final beforeEncryption = DateTime.now();
|
||||||
|
|
||||||
|
final encryptedBigFileResult = await cryptoRepository.encrypt(
|
||||||
|
dummyBytes,
|
||||||
|
sk.ok!,
|
||||||
|
);
|
||||||
|
|
||||||
|
final afterEncryption = DateTime.now();
|
||||||
|
|
||||||
|
final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch -
|
||||||
|
beforeEncryption.millisecondsSinceEpoch;
|
||||||
|
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
LogInfo(
|
||||||
|
'[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
csvLine.write('$benchmarkEncryption');
|
||||||
|
|
||||||
|
if (encryptedBigFileResult.isErr) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
LogError(
|
||||||
|
'Encryption failed: ${encryptedBigFileResult.err?.message}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
BenchmarkState.failure(
|
||||||
|
encryptedBigFileResult.err?.message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption
|
||||||
|
final beforeDecryption = DateTime.now();
|
||||||
|
await cryptoRepository.decrypt(
|
||||||
|
encryptedBigFileResult.ok!,
|
||||||
|
sk.ok!,
|
||||||
|
);
|
||||||
|
final afterDecryption = DateTime.now();
|
||||||
|
final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch -
|
||||||
|
beforeDecryption.millisecondsSinceEpoch;
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
LogInfo(
|
||||||
|
'[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
csvLine.write(';$benchmarkDecryption');
|
||||||
|
csv.writeln(csvLine);
|
||||||
|
}
|
||||||
|
debugPrint(csv.toString());
|
||||||
|
emit(
|
||||||
|
const BenchmarkState.success(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2019-2023 Hugo Pointcheval
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
part of 'benchmark_cubit.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class BenchmarkState {
|
||||||
|
const BenchmarkState.initial()
|
||||||
|
: state = State.initial,
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
const BenchmarkState.loading()
|
||||||
|
: state = State.loading,
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
const BenchmarkState.failure(this.error) : state = State.failure;
|
||||||
|
|
||||||
|
const BenchmarkState.success()
|
||||||
|
: state = State.success,
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
final State state;
|
||||||
|
final String? error;
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2019-2023 Hugo Pointcheval
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:native_crypto_example/core/typography.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
||||||
|
import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart';
|
||||||
|
import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
|
||||||
|
import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
|
||||||
|
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||||
|
|
||||||
|
class BenchmarkStateManagement
|
||||||
|
extends CubitScreen<BenchmarkCubit, BenchmarkState> {
|
||||||
|
const BenchmarkStateManagement({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
BenchmarkCubit create(BuildContext context) => BenchmarkCubit(
|
||||||
|
sessionRepository: repo<SessionRepository>(context),
|
||||||
|
loggerRepository: repo<LoggerRepository>(context),
|
||||||
|
cryptoRepository: repo<CryptoRepository>(context),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget onBuild(BuildContext context, BenchmarkState state) => ListView(
|
||||||
|
children: [
|
||||||
|
const Logs(),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
|
'Benchmark',
|
||||||
|
style: AppTypography.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
|
'''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''',
|
||||||
|
style: AppTypography.body,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: ButtonStateManagement(
|
||||||
|
label: 'Launch',
|
||||||
|
onPressed: () => bloc(context).launchBenchmark(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
@ -5,7 +5,9 @@
|
|||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:native_crypto/native_crypto.dart';
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
@ -232,4 +234,184 @@ class AESCubit extends Cubit<AESState> {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> encryptFile() async {
|
||||||
|
emit(state.copyWith(state: State.loading));
|
||||||
|
|
||||||
|
final sk = await sessionRepository.getSessionKey();
|
||||||
|
|
||||||
|
if (sk.isErr) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No SecretKey!\n'
|
||||||
|
'Go in Key tab and generate or derive one.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: sk.err?.message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick file to encrypt
|
||||||
|
final pickFileResult = await FilePicker.platform.pickFiles();
|
||||||
|
|
||||||
|
if (pickFileResult == null) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No file selected.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: 'No file selected.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final file = File(pickFileResult.files.single.path!);
|
||||||
|
|
||||||
|
// Pick folder to store the encrypted file
|
||||||
|
final resultFolder = await FilePicker.platform.getDirectoryPath();
|
||||||
|
|
||||||
|
if (resultFolder == null) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No folder selected.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: 'No folder selected.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final folder = Directory(resultFolder);
|
||||||
|
|
||||||
|
final encryption = await cryptoRepository.encryptFile(
|
||||||
|
file,
|
||||||
|
folder.uri,
|
||||||
|
sk.ok!,
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
await encryption.foldAsync(
|
||||||
|
(_) async {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogInfo('File successfully encrypted.\n'),
|
||||||
|
);
|
||||||
|
return state.copyWith(
|
||||||
|
state: State.success,
|
||||||
|
plainTextFile: '',
|
||||||
|
cipherTextFile: '',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error) async {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
LogError(error.message ?? 'Error during encryption.'),
|
||||||
|
);
|
||||||
|
return state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: error.message,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> decryptFile() async {
|
||||||
|
emit(state.copyWith(state: State.loading));
|
||||||
|
|
||||||
|
final sk = await sessionRepository.getSessionKey();
|
||||||
|
|
||||||
|
if (sk.isErr) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No SecretKey!\n'
|
||||||
|
'Go in Key tab and generate or derive one.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: sk.err?.message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await FilePicker.platform.clearTemporaryFiles();
|
||||||
|
|
||||||
|
final resultPickFile = await FilePicker.platform.pickFiles();
|
||||||
|
|
||||||
|
if (resultPickFile == null) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No file selected.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: 'No file selected.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final file = File(resultPickFile.files.single.path!);
|
||||||
|
|
||||||
|
// Pick folder to store the encrypted file
|
||||||
|
final resultFolder = await FilePicker.platform.getDirectoryPath();
|
||||||
|
|
||||||
|
if (resultFolder == null) {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogError('No folder selected.'),
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: 'No folder selected.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final folder = Directory(resultFolder);
|
||||||
|
|
||||||
|
final decryption =
|
||||||
|
await cryptoRepository.decryptFile(file, folder.uri, sk.ok!);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
await decryption.foldAsync(
|
||||||
|
(_) async {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
const LogInfo('File successfully decrypted.\n'),
|
||||||
|
);
|
||||||
|
return state.copyWith(
|
||||||
|
state: State.success,
|
||||||
|
plainTextFile: '',
|
||||||
|
cipherTextFile: '',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error) async {
|
||||||
|
await loggerRepository.addLog(
|
||||||
|
LogError(error.message ?? 'Error during decryption.'),
|
||||||
|
);
|
||||||
|
return state.copyWith(
|
||||||
|
state: State.failure,
|
||||||
|
error: error.message,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen<AESCubit, AESState> {
|
|||||||
onPressed: () => bloc(context).decryptFromMemory(),
|
onPressed: () => bloc(context).decryptFromMemory(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
|
'File',
|
||||||
|
style: AppTypography.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: ButtonStateManagement(
|
||||||
|
label: 'Encrypt file',
|
||||||
|
onPressed: () => bloc(context).encryptFile(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: ButtonStateManagement(
|
||||||
|
label: 'Decrypt file',
|
||||||
|
onPressed: () => bloc(context).decryptFile(),
|
||||||
|
),
|
||||||
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -8,7 +8,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
|
||||||
import 'package:native_crypto_example/domain/entities/mode.dart';
|
import 'package:native_crypto_example/domain/entities/mode.dart';
|
||||||
|
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
|
||||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
|
||||||
|
|
||||||
part 'mode_switcher_state.dart';
|
part 'mode_switcher_state.dart';
|
||||||
@ -16,8 +18,11 @@ part 'mode_switcher_state.dart';
|
|||||||
class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
|
class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
|
||||||
ModeSwitcherCubit(
|
ModeSwitcherCubit(
|
||||||
this.sessionRepository,
|
this.sessionRepository,
|
||||||
|
this.cryptoRepository,
|
||||||
) : super(const ModeSwitcherState(NativeCryptoMode()));
|
) : super(const ModeSwitcherState(NativeCryptoMode()));
|
||||||
|
|
||||||
SessionRepository sessionRepository;
|
SessionRepository sessionRepository;
|
||||||
|
CryptoRepository cryptoRepository;
|
||||||
|
|
||||||
FutureOr<void> switchMode() async {
|
FutureOr<void> switchMode() async {
|
||||||
final currentMode = await sessionRepository.getCurrentMode();
|
final currentMode = await sessionRepository.getCurrentMode();
|
||||||
@ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessionRepository.setCurrentMode(newMode);
|
sessionRepository.setCurrentMode(newMode);
|
||||||
|
if (cryptoRepository is CryptoRepositorySwitchableImpl) {
|
||||||
|
(cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newMode = const NativeCryptoMode();
|
newMode = const NativeCryptoMode();
|
||||||
sessionRepository.setCurrentMode(newMode);
|
sessionRepository.setCurrentMode(newMode);
|
||||||
|
|
||||||
|
if (cryptoRepository is CryptoRepositorySwitchableImpl) {
|
||||||
|
(cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(ModeSwitcherState(newMode));
|
emit(ModeSwitcherState(newMode));
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart';
|
||||||
import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart';
|
import 'package:native_crypto_example/presentation/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/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/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/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/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/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:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart';
|
||||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||||
@ -24,7 +24,7 @@ class HomeStateManagement
|
|||||||
HashStateManagement(),
|
HashStateManagement(),
|
||||||
AESStateManagement(),
|
AESStateManagement(),
|
||||||
TestVectorsStateManagement(),
|
TestVectorsStateManagement(),
|
||||||
const Blank()
|
const BenchmarkStateManagement(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -22,12 +22,12 @@ class AppBarStateManagement
|
|||||||
: 'PointyCastle',
|
: 'PointyCastle',
|
||||||
),
|
),
|
||||||
backgroundColor: state.currentMode.primaryColor,
|
backgroundColor: state.currentMode.primaryColor,
|
||||||
// TODO(hpcl): enable mode switcher
|
actions: [
|
||||||
// actions: [
|
Switch(
|
||||||
// Switch(
|
activeColor: Colors.white,
|
||||||
// value: state.currentMode == const NativeCryptoMode(),
|
value: state.currentMode == const NativeCryptoMode(),
|
||||||
// onChanged: (_) => bloc(context).switchMode(),
|
onChanged: (_) => bloc(context).switchMode(),
|
||||||
// )
|
)
|
||||||
// ],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ dependencies:
|
|||||||
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
|
file_picker: ^5.2.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test: { sdk: flutter }
|
flutter_test: { sdk: flutter }
|
||||||
|
@ -8,10 +8,13 @@
|
|||||||
enum Encoding {
|
enum Encoding {
|
||||||
/// UTF-8 encoding, as defined by the Unicode standard.
|
/// UTF-8 encoding, as defined by the Unicode standard.
|
||||||
utf8,
|
utf8,
|
||||||
|
|
||||||
/// UTF-16 encoding, as defined by the Unicode standard.
|
/// UTF-16 encoding, as defined by the Unicode standard.
|
||||||
utf16,
|
utf16,
|
||||||
|
|
||||||
/// Base64 encoding, as defined by RFC 4648.
|
/// Base64 encoding, as defined by RFC 4648.
|
||||||
base64,
|
base64,
|
||||||
|
|
||||||
/// Hexadecimal encoding.
|
/// Hexadecimal encoding.
|
||||||
base16,
|
base16,
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
enum HashAlgorithm {
|
enum HashAlgorithm {
|
||||||
/// The SHA-256 hash algorithm.
|
/// The SHA-256 hash algorithm.
|
||||||
sha256,
|
sha256,
|
||||||
|
|
||||||
/// The SHA-384 hash algorithm.
|
/// The SHA-384 hash algorithm.
|
||||||
sha384,
|
sha384,
|
||||||
|
|
||||||
/// The SHA-512 hash algorithm.
|
/// The SHA-512 hash algorithm.
|
||||||
sha512,
|
sha512,
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import 'package:native_crypto/src/domain/cipher_chunk.dart';
|
|||||||
abstract class Cipher<T extends CipherChunk> {
|
abstract class Cipher<T extends CipherChunk> {
|
||||||
/// {@macro cipher}
|
/// {@macro cipher}
|
||||||
const Cipher();
|
const Cipher();
|
||||||
|
|
||||||
/// Encrypts a [Uint8List] and returns a [CipherText].
|
/// Encrypts a [Uint8List] and returns a [CipherText].
|
||||||
Future<CipherText<T>> encrypt(Uint8List plainText);
|
Future<CipherText<T>> encrypt(Uint8List plainText);
|
||||||
|
|
||||||
|
@ -215,13 +215,15 @@ class MockNativeCryptoAPI implements NativeCryptoAPI {
|
|||||||
HashAlgorithm argAlgorithm,
|
HashAlgorithm argAlgorithm,
|
||||||
) {
|
) {
|
||||||
if (pbkdf2Fn != null) {
|
if (pbkdf2Fn != null) {
|
||||||
return Future.value(pbkdf2Fn!(
|
return Future.value(
|
||||||
argPassword,
|
pbkdf2Fn!(
|
||||||
argSalt,
|
argPassword,
|
||||||
argIterations,
|
argSalt,
|
||||||
argLength,
|
argIterations,
|
||||||
argAlgorithm.toString(),
|
argLength,
|
||||||
),);
|
argAlgorithm.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return Future.value(Uint8List.fromList([1, 2, 3]));
|
return Future.value(Uint8List.fromList([1, 2, 3]));
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ NativeCrypto - Android Implementation
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 Hugo Pointcheval
|
Copyright (c) 2019 - 2023 Hugo Pointcheval
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
# native_crypto_android
|
# NativeCrypto - Android Implementation
|
||||||
|
|
||||||
A new flutter plugin project.
|
Android Implementation of [NativeCrypto][1] Plugin.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
This project is a starting point for a Flutter
|
This project is a starting point for a Flutter [plug-in package][2], a specialized package that includes platform-specific implementation code for Android and/or iOS.
|
||||||
[plug-in package](https://flutter.dev/developing-packages/),
|
|
||||||
a specialized package that includes platform-specific implementation code for
|
|
||||||
Android and/or iOS.
|
|
||||||
|
|
||||||
For help getting started with Flutter, view our
|
For help getting started with Flutter, view our [online documentation][3], which offers tutorials, samples, guidance on mobile development, and a full API reference.
|
||||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
|
||||||
|
|
||||||
|
[1]: ../../README.md
|
||||||
|
[2]: https://flutter.dev/developing-packages/
|
||||||
|
[3]: https://flutter.dev/docs
|
||||||
|
@ -111,7 +111,7 @@ class NativeCrypto(private val context: Context) : NativeCryptoAPI {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
// For now, only AES is supported
|
// For now, only AES is supported
|
||||||
val aes = AES()
|
val aes = AES()
|
||||||
val params = FileParameters(context, plainTextPath, cipherTextPath)
|
val params = FileParameters(context, cipherTextPath, plainTextPath)
|
||||||
|
|
||||||
return aes.decryptFile(params, key)
|
return aes.decryptFile(params, key)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ package fr.pointcheval.native_crypto_android.ciphers
|
|||||||
|
|
||||||
import fr.pointcheval.native_crypto_android.interfaces.Cipher
|
import fr.pointcheval.native_crypto_android.interfaces.Cipher
|
||||||
import fr.pointcheval.native_crypto_android.utils.FileParameters
|
import fr.pointcheval.native_crypto_android.utils.FileParameters
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import javax.crypto.CipherInputStream
|
||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
import javax.crypto.spec.GCMParameterSpec
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
@ -60,28 +63,32 @@ class AES : Cipher {
|
|||||||
cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk)
|
cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk)
|
||||||
}
|
}
|
||||||
|
|
||||||
var len: Int?
|
val input = BufferedInputStream(fileParameters.getFileInputStream())
|
||||||
val buffer = ByteArray(8192)
|
|
||||||
val inputFile = fileParameters.getFileInputStream()
|
|
||||||
val outputFile = fileParameters.getFileOutputStream()
|
|
||||||
val iv: ByteArray? = cipherInstance!!.iv
|
|
||||||
|
|
||||||
outputFile?.write(iv)
|
var outputBuffered = BufferedOutputStream(fileParameters.getFileOutputStream(false))
|
||||||
outputFile?.flush()
|
val iv: ByteArray = cipherInstance!!.iv
|
||||||
|
|
||||||
|
// Prepend the IV to the cipherText file
|
||||||
|
outputBuffered.write(iv)
|
||||||
|
outputBuffered.flush()
|
||||||
|
outputBuffered.close()
|
||||||
|
|
||||||
|
// Reopen the file and append the cipherText
|
||||||
|
outputBuffered = BufferedOutputStream(fileParameters.getFileOutputStream(true))
|
||||||
|
val output = CipherOutputStream(outputBuffered, cipherInstance)
|
||||||
|
|
||||||
|
var i: Int
|
||||||
|
do {
|
||||||
|
i = input.read()
|
||||||
|
if (i != -1) output.write(i)
|
||||||
|
} while (i != -1)
|
||||||
|
|
||||||
|
output.flush()
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
input.close()
|
||||||
|
outputBuffered.close()
|
||||||
|
|
||||||
val encryptedStream = CipherOutputStream(outputFile!!, cipherInstance)
|
|
||||||
while(true) {
|
|
||||||
len = inputFile?.read(buffer)
|
|
||||||
if (len != null && len > 0) {
|
|
||||||
encryptedStream.write(buffer,0,len)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encryptedStream.flush()
|
|
||||||
encryptedStream.close()
|
|
||||||
inputFile?.close()
|
|
||||||
outputFile.close()
|
|
||||||
|
|
||||||
return fileParameters.outputExists()
|
return fileParameters.outputExists()
|
||||||
}
|
}
|
||||||
@ -110,9 +117,7 @@ class AES : Cipher {
|
|||||||
val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV")
|
val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV")
|
||||||
|
|
||||||
// Read the first 12 bytes from the file
|
// Read the first 12 bytes from the file
|
||||||
for (i in 0 until 12) {
|
inputFile.read(iv)
|
||||||
iv[i] = inputFile.read().toByte()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize secret key spec
|
// Initialize secret key spec
|
||||||
val sk = SecretKeySpec(key, "AES")
|
val sk = SecretKeySpec(key, "AES")
|
||||||
@ -125,21 +130,19 @@ class AES : Cipher {
|
|||||||
|
|
||||||
cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec)
|
cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec)
|
||||||
|
|
||||||
var len: Int?
|
val input = CipherInputStream(BufferedInputStream(inputFile), cipherInstance)
|
||||||
val buffer = ByteArray(8192)
|
val output = BufferedOutputStream(fileParameters.getFileOutputStream(false))
|
||||||
val outputFile = fileParameters.getFileOutputStream()
|
|
||||||
val decryptedStream = CipherOutputStream(outputFile!!, cipherInstance)
|
var i: Int
|
||||||
while (true) {
|
do {
|
||||||
len = inputFile.read(buffer)
|
i = input.read()
|
||||||
if(len > 0){
|
if (i != -1) output.write(i)
|
||||||
decryptedStream.write(buffer,0, len)
|
} while (i != -1)
|
||||||
} else {
|
|
||||||
break
|
output.flush()
|
||||||
}
|
output.close()
|
||||||
}
|
|
||||||
decryptedStream.flush()
|
input.close()
|
||||||
decryptedStream.close()
|
|
||||||
inputFile.close()
|
|
||||||
|
|
||||||
return fileParameters.outputExists()
|
return fileParameters.outputExists()
|
||||||
}
|
}
|
||||||
|
@ -43,14 +43,15 @@ class FileParameters(ctx: Context, input: String, output: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFileOutputStream(): OutputStream? {
|
fun getFileOutputStream(append: Boolean): OutputStream? {
|
||||||
val path = outputPath
|
val path = outputPath
|
||||||
return try{
|
return try{
|
||||||
FileOutputStream(path)
|
FileOutputStream(path, append)
|
||||||
} catch(e: IOException){
|
} catch(e: IOException){
|
||||||
val documentFile: DocumentFile = this.getDocumentFileByPath(path)
|
val documentFile: DocumentFile = this.getDocumentFileByPath(path)
|
||||||
val documentUri = documentFile.uri
|
val documentUri = documentFile.uri
|
||||||
context.contentResolver.openOutputStream(documentUri)
|
val mode = if(append) "wa" else "w"
|
||||||
|
context.contentResolver.openOutputStream(documentUri, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ NativeCrypto - iOS Implementation
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 Hugo Pointcheval
|
Copyright (c) 2019 - 2023 Hugo Pointcheval
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
# NativeCrypto - iOS Implementation
|
# NativeCrypto - iOS Implementation
|
||||||
|
|
||||||
iOS Implementation of NativeCrypto Plugin.
|
iOS Implementation of [NativeCrypto][1] Plugin.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
This project is a starting point for a Flutter
|
This project is a starting point for a Flutter [plug-in package][2], a specialized package that includes platform-specific implementation code for Android and/or iOS.
|
||||||
[plug-in package](https://flutter.dev/developing-packages/),
|
|
||||||
a specialized package that includes platform-specific implementation code for
|
|
||||||
Android and/or iOS.
|
|
||||||
|
|
||||||
For help getting started with Flutter, view our
|
For help getting started with Flutter, view our [online documentation][3], which offers tutorials, samples, guidance on mobile development, and a full API reference.
|
||||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
|
||||||
|
|
||||||
|
[1]: ../../README.md
|
||||||
|
[2]: https://flutter.dev/developing-packages/
|
||||||
|
[3]: https://flutter.dev/docs
|
||||||
|
@ -25,7 +25,6 @@ public class NativeCrypto: NSObject, NativeCryptoAPI {
|
|||||||
case .sha256: return FlutterStandardTypedData(bytes: Data(HMAC<SHA256>(key: symmetricKey).finalize()))
|
case .sha256: return FlutterStandardTypedData(bytes: Data(HMAC<SHA256>(key: symmetricKey).finalize()))
|
||||||
case .sha384: return FlutterStandardTypedData(bytes: Data(HMAC<SHA384>(key: symmetricKey).finalize()))
|
case .sha384: return FlutterStandardTypedData(bytes: Data(HMAC<SHA384>(key: symmetricKey).finalize()))
|
||||||
case .sha512: return FlutterStandardTypedData(bytes: Data(HMAC<SHA512>(key: symmetricKey).finalize()))
|
case .sha512: return FlutterStandardTypedData(bytes: Data(HMAC<SHA512>(key: symmetricKey).finalize()))
|
||||||
@unknown default: fatalError("Unknown algorithm")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ public class HashAlgorithmParser {
|
|||||||
case .sha256: return SHA256.init()
|
case .sha256: return SHA256.init()
|
||||||
case .sha384: return SHA384.init()
|
case .sha384: return SHA384.init()
|
||||||
case .sha512: return SHA512.init()
|
case .sha512: return SHA512.init()
|
||||||
@unknown default: fatalError("Unknown algorithm")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ public class HashAlgorithmParser {
|
|||||||
case .sha256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256)
|
case .sha256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256)
|
||||||
case .sha384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384)
|
case .sha384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384)
|
||||||
case .sha512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512)
|
case .sha512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512)
|
||||||
@unknown default: fatalError("Unknown algorithm")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
class Task<T> {
|
|
||||||
|
|
||||||
var task: () throws -> T
|
|
||||||
private var successful: Bool = false
|
|
||||||
private var result: T? = nil
|
|
||||||
private var exception: Error? = nil
|
|
||||||
|
|
||||||
init(task: @escaping () throws -> T) {
|
|
||||||
self.task = task
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSuccessful() -> Bool {
|
|
||||||
return successful
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResult() -> T? {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func getException() -> Error {
|
|
||||||
if (exception != nil) {
|
|
||||||
return exception!
|
|
||||||
} else {
|
|
||||||
return NativeCryptoError.unknownError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func call() {
|
|
||||||
do {
|
|
||||||
result = try task()
|
|
||||||
exception = nil
|
|
||||||
successful = true
|
|
||||||
} catch {
|
|
||||||
exception = error
|
|
||||||
result = nil
|
|
||||||
successful = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func finalize(callback: (_ task: Task<T>) -> Void) {
|
|
||||||
callback(self)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,12 @@
|
|||||||
# NativeCrypto - Platform Interface
|
# NativeCrypto - Platform Interface
|
||||||
|
|
||||||
A common platform interface for the [`native_crypto`][1] plugin.
|
A common platform interface for the [NativeCrypto][1] plugin.
|
||||||
|
|
||||||
This interface allows platform-specific implementations of the `native_crypto` plugin, as well as the plugin itself, to ensure they are supporting the same interface.
|
This interface allows platform-specific implementations of the `native_crypto` plugin, as well as the plugin itself, to ensure they are supporting the same interface.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`.
|
To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][1] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`.
|
||||||
|
|
||||||
## Pigeon
|
## Pigeon
|
||||||
|
|
||||||
@ -16,5 +16,5 @@ Run generator with `flutter pub run pigeon --input pigeons/messages.dart`.
|
|||||||
|
|
||||||
> Note: Make sure the `lib/src/gen` folder exists before running the generator.
|
> Note: Make sure the `lib/src/gen` folder exists before running the generator.
|
||||||
|
|
||||||
[1]: ../native_crypto
|
[1]: ../../README.md
|
||||||
[2]: lib/native_crypto_platform_interface.dart
|
[2]: lib/native_crypto_platform_interface.dart
|
||||||
|
Loading…
x
Reference in New Issue
Block a user