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">
 | 
			
		||||
<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>
 | 
			
		||||
@ -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.
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
 | 
			
		||||
#### Compatibility
 | 
			
		||||
 | 
			
		||||
First, check compatibility with your targets.
 | 
			
		||||
 | 
			
		||||
| 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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
#### 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
 | 
			
		||||
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');
 | 
			
		||||
SecretKet secretKey = SecretKey.fromUtf16('secret');
 | 
			
		||||
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
 | 
			
		||||
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
 | 
			
		||||
@ -79,20 +165,21 @@ You can derive a `SecretKey` using **PBKDF2**.
 | 
			
		||||
First, you need to initialize a `Pbkdf2` object.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
Pbkdf2 pbkdf2 = Pbkdf2(
 | 
			
		||||
    keyBytesCount: 32,
 | 
			
		||||
final Pbkdf2 pbkdf2 = Pbkdf2(
 | 
			
		||||
    length: 32, // 32 bytes
 | 
			
		||||
    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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -101,44 +188,79 @@ 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);
 | 
			
		||||
final AES cipher = AES(
 | 
			
		||||
    key: key,
 | 
			
		||||
    mode: AESMode.gcm,
 | 
			
		||||
    padding: AESPadding.none,
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message); 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
You have to reconstruct the wrapper before decrypting.
 | 
			
		||||
Uppon receiving encrypted message `receivedData` , you can decrypt it.
 | 
			
		||||
You have to reconstruct the ciphertext and the setup the chunk factory.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
CipherTextWrapper wrapper = CipherTextWrapper.fromBytes(
 | 
			
		||||
    data,
 | 
			
		||||
    ivLength: AESMode.gcm.ivLength,
 | 
			
		||||
    tagLength: AESMode.gcm.tagLength,
 | 
			
		||||
);
 | 
			
		||||
final CipherText<AESCipherChunk> receivedCipherText CipherText(
 | 
			
		||||
    receivedData,
 | 
			
		||||
    chunkFactory: (bytes) => AESCipherChunk(
 | 
			
		||||
        bytes,
 | 
			
		||||
        ivLength: cipher.mode.ivLength,
 | 
			
		||||
        tagLength: cipher.mode.tagLength,
 | 
			
		||||
    ),
 | 
			
		||||
),
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then, you can decrypt your message.
 | 
			
		||||
 | 
			
		||||
```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
 | 
			
		||||
 | 
			
		||||
### Android
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ NativeCrypto
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 | 
			
		||||
@ -1,133 +1,3 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
<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>
 | 
			
		||||
# NativeCrypto
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
<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);
 | 
			
		||||
```
 | 
			
		||||
Readme available at [project root](../../README.md).
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,74 @@
 | 
			
		||||
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)
 | 
			
		||||
  - native_crypto_ios (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - SDWebImage (5.15.5):
 | 
			
		||||
    - SDWebImage/Core (= 5.15.5)
 | 
			
		||||
  - SDWebImage/Core (5.15.5)
 | 
			
		||||
  - SwiftyGif (5.4.4)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES:
 | 
			
		||||
  - file_picker (from `.symlinks/plugins/file_picker/ios`)
 | 
			
		||||
  - Flutter (from `Flutter`)
 | 
			
		||||
  - native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`)
 | 
			
		||||
 | 
			
		||||
SPEC REPOS:
 | 
			
		||||
  trunk:
 | 
			
		||||
    - DKImagePickerController
 | 
			
		||||
    - DKPhotoGallery
 | 
			
		||||
    - SDWebImage
 | 
			
		||||
    - SwiftyGif
 | 
			
		||||
 | 
			
		||||
EXTERNAL SOURCES:
 | 
			
		||||
  file_picker:
 | 
			
		||||
    :path: ".symlinks/plugins/file_picker/ios"
 | 
			
		||||
  Flutter:
 | 
			
		||||
    :path: Flutter
 | 
			
		||||
  native_crypto_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/native_crypto_ios/ios"
 | 
			
		||||
 | 
			
		||||
SPEC CHECKSUMS:
 | 
			
		||||
  DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
 | 
			
		||||
  DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
 | 
			
		||||
  file_picker: ce3938a0df3cc1ef404671531facef740d03f920
 | 
			
		||||
  Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
 | 
			
		||||
  native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
 | 
			
		||||
  SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
 | 
			
		||||
  SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
 | 
			
		||||
 | 
			
		||||
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,5 +47,9 @@
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UIApplicationSupportsIndirectInputEvents</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UISupportsDocumentBrowser</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>LSSupportsOpeningDocumentsInPlace</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
 | 
			
		||||
@ -30,6 +32,29 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
 | 
			
		||||
    return plainText;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final AES cipher = AES(
 | 
			
		||||
      key: key,
 | 
			
		||||
      mode: AESMode.gcm,
 | 
			
		||||
      padding: AESPadding.none,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final plainText = File.fromUri(
 | 
			
		||||
      Uri.parse(
 | 
			
		||||
        '${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}',
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    await cipher.decryptFile(
 | 
			
		||||
      cipherText,
 | 
			
		||||
      plainText,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<SecretKey> deriveKeyFromPassword(
 | 
			
		||||
    String password, {
 | 
			
		||||
@ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
 | 
			
		||||
    return cipherText.bytes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final AES cipher = AES(
 | 
			
		||||
      key: key,
 | 
			
		||||
      mode: AESMode.gcm,
 | 
			
		||||
      padding: AESPadding.none,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final cipherText = File.fromUri(
 | 
			
		||||
      Uri.parse(
 | 
			
		||||
        '${folderResult.path}/${plainText.path.split('/').last}.enc',
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await cipher.encryptFile(plainText, cipherText);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<SecretKey> generateSecureRandom(int length) async {
 | 
			
		||||
    final SecretKey sk = await SecretKey.fromSecureRandom(length);
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,8 @@
 | 
			
		||||
 | 
			
		||||
// ignore_for_file: implementation_imports
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
 | 
			
		||||
@ -37,6 +39,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
 | 
			
		||||
    return paddedPlainText;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) async {
 | 
			
		||||
    throw UnimplementedError();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<SecretKey> deriveKeyFromPassword(
 | 
			
		||||
    String password, {
 | 
			
		||||
@ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<Uint8List> encryptWithIV(
 | 
			
		||||
      Uint8List data, SecretKey key, Uint8List iv,) async {
 | 
			
		||||
    Uint8List data,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
    Uint8List iv,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final gcm = GCMBlockCipher(AESEngine())
 | 
			
		||||
      ..init(
 | 
			
		||||
        true,
 | 
			
		||||
@ -144,6 +158,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) async {
 | 
			
		||||
    throw UnimplementedError();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<SecretKey> generateSecureRandom(int length) async {
 | 
			
		||||
    if (_secureRandom == null) {
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
import 'dart:typed_data';
 | 
			
		||||
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
@ -30,6 +31,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<SecretKey> deriveKeyFromPassword(
 | 
			
		||||
    String password, {
 | 
			
		||||
@ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> encryptWithIV(
 | 
			
		||||
          Uint8List data, SecretKey key, Uint8List iv,) =>
 | 
			
		||||
    Uint8List data,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
    Uint8List iv,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.encryptWithIV(data, key, iv),
 | 
			
		||||
        (error) {
 | 
			
		||||
@ -97,6 +117,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.encryptFile(plainText, folderResult, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<SecretKey> generateSecureRandom(int length) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,167 @@
 | 
			
		||||
// Copyright 2019-2023 Hugo Pointcheval
 | 
			
		||||
//
 | 
			
		||||
// Use of this source code is governed by an MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
import 'dart:typed_data';
 | 
			
		||||
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/entities/mode.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
 | 
			
		||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
 | 
			
		||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
 | 
			
		||||
 | 
			
		||||
class CryptoRepositorySwitchableImpl extends CryptoRepository {
 | 
			
		||||
  CryptoRepositorySwitchableImpl({
 | 
			
		||||
    required this.nativeCryptoDataSource,
 | 
			
		||||
    required this.pointyCastleDataSource,
 | 
			
		||||
    required this.currentMode,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  CryptoDataSource nativeCryptoDataSource;
 | 
			
		||||
  CryptoDataSource pointyCastleDataSource;
 | 
			
		||||
  Mode currentMode;
 | 
			
		||||
 | 
			
		||||
  set mode(Mode mode) {
 | 
			
		||||
    currentMode = mode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CryptoDataSource get cryptoDataSource {
 | 
			
		||||
    if (currentMode is NativeCryptoMode) {
 | 
			
		||||
      return nativeCryptoDataSource;
 | 
			
		||||
    } else if (currentMode is PointyCastleMode) {
 | 
			
		||||
      return pointyCastleDataSource;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw Exception('Unknown mode');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.decrypt(data, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<SecretKey> deriveKeyFromPassword(
 | 
			
		||||
    String password, {
 | 
			
		||||
    required String salt,
 | 
			
		||||
  }) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.deriveKeyFromPassword(
 | 
			
		||||
          password,
 | 
			
		||||
          salt: salt,
 | 
			
		||||
        ),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.hash(hasher, data),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.hmac(hmac, data, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.encrypt(data, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<Uint8List> encryptWithIV(
 | 
			
		||||
    Uint8List data,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
    Uint8List iv,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.encryptWithIV(data, key, iv),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  ) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.encryptFile(plainText, folderResult, key),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<SecretKey> generateSecureRandom(int length) =>
 | 
			
		||||
      Result.tryCatchAsync(
 | 
			
		||||
        () async => cryptoDataSource.generateSecureRandom(length),
 | 
			
		||||
        (error) {
 | 
			
		||||
          if (error is NativeCryptoException) {
 | 
			
		||||
            return ClientException('${error.message}');
 | 
			
		||||
          }
 | 
			
		||||
          return ClientException(error.toString());
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
}
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
 | 
			
		||||
@ -15,12 +17,22 @@ abstract class CryptoDataSource extends BaseDataSource {
 | 
			
		||||
    required String salt,
 | 
			
		||||
  });
 | 
			
		||||
  Future<Uint8List> encrypt(Uint8List data, SecretKey key);
 | 
			
		||||
  Future<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  );
 | 
			
		||||
  Future<Uint8List> encryptWithIV(
 | 
			
		||||
    Uint8List data,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
    Uint8List iv,
 | 
			
		||||
  );
 | 
			
		||||
  Future<Uint8List> decrypt(Uint8List data, SecretKey key);
 | 
			
		||||
  Future<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  );
 | 
			
		||||
  Future<Uint8List> hash(Hash hasher, Uint8List data);
 | 
			
		||||
  Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
 | 
			
		||||
@ -15,12 +17,22 @@ abstract class CryptoRepository extends BaseRepository {
 | 
			
		||||
    required String salt,
 | 
			
		||||
  });
 | 
			
		||||
  FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key);
 | 
			
		||||
  FutureOrResult<void> encryptFile(
 | 
			
		||||
    File plainText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  );
 | 
			
		||||
  FutureOrResult<Uint8List> encryptWithIV(
 | 
			
		||||
    Uint8List data,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
    Uint8List iv,
 | 
			
		||||
  );
 | 
			
		||||
  FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key);
 | 
			
		||||
  FutureOrResult<void> decryptFile(
 | 
			
		||||
    File cipherText,
 | 
			
		||||
    Uri folderResult,
 | 
			
		||||
    SecretKey key,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data);
 | 
			
		||||
  FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,11 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_bloc/flutter_bloc.dart';
 | 
			
		||||
import 'package:native_crypto_example/core/get_it.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/repositories/session_repository_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/entities/mode.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
 | 
			
		||||
@ -28,6 +30,12 @@ class App extends StatelessWidget {
 | 
			
		||||
  final SessionRepository _sessionRepository =
 | 
			
		||||
      SessionRepositoryImpl(sessionDataSource: getIt());
 | 
			
		||||
 | 
			
		||||
  final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl(
 | 
			
		||||
    nativeCryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
 | 
			
		||||
    pointyCastleDataSource: getIt<PointyCastleDataSourceImpl>(),
 | 
			
		||||
    currentMode: const NativeCryptoMode(),
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) => MultiProvider(
 | 
			
		||||
        repositoryProviders: [
 | 
			
		||||
@ -35,18 +43,17 @@ class App extends StatelessWidget {
 | 
			
		||||
          RepositoryProvider<SessionRepository>.value(
 | 
			
		||||
            value: _sessionRepository,
 | 
			
		||||
          ),
 | 
			
		||||
          RepositoryProvider<CryptoRepository>(
 | 
			
		||||
            create: (_) => CryptoRepositoryImpl(
 | 
			
		||||
              cryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          RepositoryProvider<CryptoRepository>.value(value: _cryptoRepository),
 | 
			
		||||
        ],
 | 
			
		||||
        blocProviders: [
 | 
			
		||||
          BlocProvider<OutputCubit>(
 | 
			
		||||
            create: (_) => OutputCubit(_loggerRepository),
 | 
			
		||||
          ),
 | 
			
		||||
          BlocProvider<ModeSwitcherCubit>(
 | 
			
		||||
            create: (_) => ModeSwitcherCubit(_sessionRepository),
 | 
			
		||||
            create: (_) => ModeSwitcherCubit(
 | 
			
		||||
              _sessionRepository,
 | 
			
		||||
              _cryptoRepository,
 | 
			
		||||
            ),
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
        child: MaterialApp(
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,132 @@
 | 
			
		||||
// Copyright 2019-2023 Hugo Pointcheval
 | 
			
		||||
//
 | 
			
		||||
// Use of this source code is governed by an MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter_bloc/flutter_bloc.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/entities/log_message.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/entities/states.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
 | 
			
		||||
 | 
			
		||||
part 'benchmark_state.dart';
 | 
			
		||||
 | 
			
		||||
class BenchmarkCubit extends Cubit<BenchmarkState> {
 | 
			
		||||
  BenchmarkCubit({
 | 
			
		||||
    required this.sessionRepository,
 | 
			
		||||
    required this.loggerRepository,
 | 
			
		||||
    required this.cryptoRepository,
 | 
			
		||||
  }) : super(const BenchmarkState.initial());
 | 
			
		||||
  final SessionRepository sessionRepository;
 | 
			
		||||
  final LoggerRepository loggerRepository;
 | 
			
		||||
  final CryptoRepository cryptoRepository;
 | 
			
		||||
 | 
			
		||||
  List<int> testedSizes = [
 | 
			
		||||
    2097152,
 | 
			
		||||
    6291456,
 | 
			
		||||
    10485760,
 | 
			
		||||
    14680064,
 | 
			
		||||
    18874368,
 | 
			
		||||
    23068672,
 | 
			
		||||
    27262976,
 | 
			
		||||
    31457280,
 | 
			
		||||
    35651584,
 | 
			
		||||
    39845888,
 | 
			
		||||
    44040192,
 | 
			
		||||
    48234496,
 | 
			
		||||
    52428800,
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  FutureOr<void> launchBenchmark() async {
 | 
			
		||||
    emit(const BenchmarkState.loading());
 | 
			
		||||
 | 
			
		||||
    final sk = await sessionRepository.getSessionKey();
 | 
			
		||||
 | 
			
		||||
    if (sk.isErr) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No SecretKey!\n'
 | 
			
		||||
            'Go in Key tab and generate or derive one.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        BenchmarkState.failure(
 | 
			
		||||
          sk.err?.message,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int run = 0;
 | 
			
		||||
    final csv = StringBuffer(
 | 
			
		||||
      'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n',
 | 
			
		||||
    );
 | 
			
		||||
    for (final size in testedSizes) {
 | 
			
		||||
      run++;
 | 
			
		||||
      final StringBuffer csvLine = StringBuffer();
 | 
			
		||||
      final dummyBytes = Uint8List(size);
 | 
			
		||||
      csvLine.write('$run;$size;');
 | 
			
		||||
 | 
			
		||||
      // Encryption
 | 
			
		||||
      final beforeEncryption = DateTime.now();
 | 
			
		||||
 | 
			
		||||
      final encryptedBigFileResult = await cryptoRepository.encrypt(
 | 
			
		||||
        dummyBytes,
 | 
			
		||||
        sk.ok!,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final afterEncryption = DateTime.now();
 | 
			
		||||
 | 
			
		||||
      final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch -
 | 
			
		||||
          beforeEncryption.millisecondsSinceEpoch;
 | 
			
		||||
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        LogInfo(
 | 
			
		||||
          '[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      csvLine.write('$benchmarkEncryption');
 | 
			
		||||
 | 
			
		||||
      if (encryptedBigFileResult.isErr) {
 | 
			
		||||
        await loggerRepository.addLog(
 | 
			
		||||
          LogError(
 | 
			
		||||
            'Encryption failed: ${encryptedBigFileResult.err?.message}',
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        emit(
 | 
			
		||||
          BenchmarkState.failure(
 | 
			
		||||
            encryptedBigFileResult.err?.message,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Decryption
 | 
			
		||||
      final beforeDecryption = DateTime.now();
 | 
			
		||||
      await cryptoRepository.decrypt(
 | 
			
		||||
        encryptedBigFileResult.ok!,
 | 
			
		||||
        sk.ok!,
 | 
			
		||||
      );
 | 
			
		||||
      final afterDecryption = DateTime.now();
 | 
			
		||||
      final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch -
 | 
			
		||||
          beforeDecryption.millisecondsSinceEpoch;
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        LogInfo(
 | 
			
		||||
          '[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      csvLine.write(';$benchmarkDecryption');
 | 
			
		||||
      csv.writeln(csvLine);
 | 
			
		||||
    }
 | 
			
		||||
    debugPrint(csv.toString());
 | 
			
		||||
    emit(
 | 
			
		||||
      const BenchmarkState.success(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
// Copyright 2019-2023 Hugo Pointcheval
 | 
			
		||||
//
 | 
			
		||||
// Use of this source code is governed by an MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
part of 'benchmark_cubit.dart';
 | 
			
		||||
 | 
			
		||||
@immutable
 | 
			
		||||
class BenchmarkState {
 | 
			
		||||
  const BenchmarkState.initial()
 | 
			
		||||
      : state = State.initial,
 | 
			
		||||
        error = null;
 | 
			
		||||
 | 
			
		||||
  const BenchmarkState.loading()
 | 
			
		||||
      : state = State.loading,
 | 
			
		||||
        error = null;
 | 
			
		||||
 | 
			
		||||
  const BenchmarkState.failure(this.error) : state = State.failure;
 | 
			
		||||
 | 
			
		||||
  const BenchmarkState.success()
 | 
			
		||||
      : state = State.success,
 | 
			
		||||
        error = null;
 | 
			
		||||
 | 
			
		||||
  final State state;
 | 
			
		||||
  final String? error;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,55 @@
 | 
			
		||||
// Copyright 2019-2023 Hugo Pointcheval
 | 
			
		||||
//
 | 
			
		||||
// Use of this source code is governed by an MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file or at
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:native_crypto_example/core/typography.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
 | 
			
		||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
 | 
			
		||||
 | 
			
		||||
class BenchmarkStateManagement
 | 
			
		||||
    extends CubitScreen<BenchmarkCubit, BenchmarkState> {
 | 
			
		||||
  const BenchmarkStateManagement({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  BenchmarkCubit create(BuildContext context) => BenchmarkCubit(
 | 
			
		||||
        sessionRepository: repo<SessionRepository>(context),
 | 
			
		||||
        loggerRepository: repo<LoggerRepository>(context),
 | 
			
		||||
        cryptoRepository: repo<CryptoRepository>(context),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget onBuild(BuildContext context, BenchmarkState state) => ListView(
 | 
			
		||||
        children: [
 | 
			
		||||
          const Logs(),
 | 
			
		||||
          const Padding(
 | 
			
		||||
            padding: EdgeInsets.all(8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              'Benchmark',
 | 
			
		||||
              style: AppTypography.title,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Padding(
 | 
			
		||||
            padding: EdgeInsets.all(8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              '''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''',
 | 
			
		||||
              style: AppTypography.body,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8),
 | 
			
		||||
            child: ButtonStateManagement(
 | 
			
		||||
              label: 'Launch',
 | 
			
		||||
              onPressed: () => bloc(context).launchBenchmark(),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
}
 | 
			
		||||
@ -5,7 +5,9 @@
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:file_picker/file_picker.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter_bloc/flutter_bloc.dart';
 | 
			
		||||
import 'package:native_crypto/native_crypto.dart';
 | 
			
		||||
@ -232,4 +234,184 @@ class AESCubit extends Cubit<AESState> {
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FutureOr<void> encryptFile() async {
 | 
			
		||||
    emit(state.copyWith(state: State.loading));
 | 
			
		||||
 | 
			
		||||
    final sk = await sessionRepository.getSessionKey();
 | 
			
		||||
 | 
			
		||||
    if (sk.isErr) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No SecretKey!\n'
 | 
			
		||||
            'Go in Key tab and generate or derive one.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: sk.err?.message,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Pick file to encrypt
 | 
			
		||||
    final pickFileResult = await FilePicker.platform.pickFiles();
 | 
			
		||||
 | 
			
		||||
    if (pickFileResult == null) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No file selected.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: 'No file selected.',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final file = File(pickFileResult.files.single.path!);
 | 
			
		||||
 | 
			
		||||
    // Pick folder to store the encrypted file
 | 
			
		||||
    final resultFolder = await FilePicker.platform.getDirectoryPath();
 | 
			
		||||
 | 
			
		||||
    if (resultFolder == null) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No folder selected.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: 'No folder selected.',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final folder = Directory(resultFolder);
 | 
			
		||||
 | 
			
		||||
    final encryption = await cryptoRepository.encryptFile(
 | 
			
		||||
      file,
 | 
			
		||||
      folder.uri,
 | 
			
		||||
      sk.ok!,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    emit(
 | 
			
		||||
      await encryption.foldAsync(
 | 
			
		||||
        (_) async {
 | 
			
		||||
          await loggerRepository.addLog(
 | 
			
		||||
            const LogInfo('File successfully encrypted.\n'),
 | 
			
		||||
          );
 | 
			
		||||
          return state.copyWith(
 | 
			
		||||
            state: State.success,
 | 
			
		||||
            plainTextFile: '',
 | 
			
		||||
            cipherTextFile: '',
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        (error) async {
 | 
			
		||||
          await loggerRepository.addLog(
 | 
			
		||||
            LogError(error.message ?? 'Error during encryption.'),
 | 
			
		||||
          );
 | 
			
		||||
          return state.copyWith(
 | 
			
		||||
            state: State.failure,
 | 
			
		||||
            error: error.message,
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FutureOr<void> decryptFile() async {
 | 
			
		||||
    emit(state.copyWith(state: State.loading));
 | 
			
		||||
 | 
			
		||||
    final sk = await sessionRepository.getSessionKey();
 | 
			
		||||
 | 
			
		||||
    if (sk.isErr) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No SecretKey!\n'
 | 
			
		||||
            'Go in Key tab and generate or derive one.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: sk.err?.message,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await FilePicker.platform.clearTemporaryFiles();
 | 
			
		||||
 | 
			
		||||
    final resultPickFile = await FilePicker.platform.pickFiles();
 | 
			
		||||
 | 
			
		||||
    if (resultPickFile == null) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No file selected.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: 'No file selected.',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final file = File(resultPickFile.files.single.path!);
 | 
			
		||||
 | 
			
		||||
    // Pick folder to store the encrypted file
 | 
			
		||||
    final resultFolder = await FilePicker.platform.getDirectoryPath();
 | 
			
		||||
 | 
			
		||||
    if (resultFolder == null) {
 | 
			
		||||
      await loggerRepository.addLog(
 | 
			
		||||
        const LogError('No folder selected.'),
 | 
			
		||||
      );
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          state: State.failure,
 | 
			
		||||
          error: 'No folder selected.',
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final folder = Directory(resultFolder);
 | 
			
		||||
 | 
			
		||||
    final decryption =
 | 
			
		||||
        await cryptoRepository.decryptFile(file, folder.uri, sk.ok!);
 | 
			
		||||
 | 
			
		||||
    emit(
 | 
			
		||||
      await decryption.foldAsync(
 | 
			
		||||
        (_) async {
 | 
			
		||||
          await loggerRepository.addLog(
 | 
			
		||||
            const LogInfo('File successfully decrypted.\n'),
 | 
			
		||||
          );
 | 
			
		||||
          return state.copyWith(
 | 
			
		||||
            state: State.success,
 | 
			
		||||
            plainTextFile: '',
 | 
			
		||||
            cipherTextFile: '',
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        (error) async {
 | 
			
		||||
          await loggerRepository.addLog(
 | 
			
		||||
            LogError(error.message ?? 'Error during decryption.'),
 | 
			
		||||
          );
 | 
			
		||||
          return state.copyWith(
 | 
			
		||||
            state: State.failure,
 | 
			
		||||
            error: error.message,
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen<AESCubit, AESState> {
 | 
			
		||||
              onPressed: () => bloc(context).decryptFromMemory(),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Padding(
 | 
			
		||||
            padding: EdgeInsets.all(8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              'File',
 | 
			
		||||
              style: AppTypography.title,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8),
 | 
			
		||||
            child: ButtonStateManagement(
 | 
			
		||||
              label: 'Encrypt file',
 | 
			
		||||
              onPressed: () => bloc(context).encryptFile(),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8),
 | 
			
		||||
            child: ButtonStateManagement(
 | 
			
		||||
              label: 'Decrypt file',
 | 
			
		||||
              onPressed: () => bloc(context).decryptFile(),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Padding(
 | 
			
		||||
            padding: EdgeInsets.all(8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,9 @@ import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter_bloc/flutter_bloc.dart';
 | 
			
		||||
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/entities/mode.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
 | 
			
		||||
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
 | 
			
		||||
 | 
			
		||||
part 'mode_switcher_state.dart';
 | 
			
		||||
@ -16,8 +18,11 @@ part 'mode_switcher_state.dart';
 | 
			
		||||
class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
 | 
			
		||||
  ModeSwitcherCubit(
 | 
			
		||||
    this.sessionRepository,
 | 
			
		||||
    this.cryptoRepository,
 | 
			
		||||
  ) : super(const ModeSwitcherState(NativeCryptoMode()));
 | 
			
		||||
 | 
			
		||||
  SessionRepository sessionRepository;
 | 
			
		||||
  CryptoRepository cryptoRepository;
 | 
			
		||||
 | 
			
		||||
  FutureOr<void> switchMode() async {
 | 
			
		||||
    final currentMode = await sessionRepository.getCurrentMode();
 | 
			
		||||
@ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      sessionRepository.setCurrentMode(newMode);
 | 
			
		||||
      if (cryptoRepository is CryptoRepositorySwitchableImpl) {
 | 
			
		||||
        (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      newMode = const NativeCryptoMode();
 | 
			
		||||
      sessionRepository.setCurrentMode(newMode);
 | 
			
		||||
 | 
			
		||||
      if (cryptoRepository is CryptoRepositorySwitchableImpl) {
 | 
			
		||||
        (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit(ModeSwitcherState(newMode));
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,12 @@
 | 
			
		||||
// https://opensource.org/licenses/MIT.
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/home/widgets/blank.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart';
 | 
			
		||||
import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart';
 | 
			
		||||
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
 | 
			
		||||
@ -24,7 +24,7 @@ class HomeStateManagement
 | 
			
		||||
    HashStateManagement(),
 | 
			
		||||
    AESStateManagement(),
 | 
			
		||||
    TestVectorsStateManagement(),
 | 
			
		||||
    const Blank()
 | 
			
		||||
    const BenchmarkStateManagement(),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
 | 
			
		||||
@ -22,12 +22,12 @@ class AppBarStateManagement
 | 
			
		||||
              : 'PointyCastle',
 | 
			
		||||
        ),
 | 
			
		||||
        backgroundColor: state.currentMode.primaryColor,
 | 
			
		||||
        // TODO(hpcl): enable mode switcher
 | 
			
		||||
        // actions: [
 | 
			
		||||
        //   Switch(
 | 
			
		||||
        //     value: state.currentMode == const NativeCryptoMode(),
 | 
			
		||||
        //     onChanged: (_) => bloc(context).switchMode(),
 | 
			
		||||
        //   )
 | 
			
		||||
        // ],
 | 
			
		||||
        actions: [
 | 
			
		||||
          Switch(
 | 
			
		||||
            activeColor: Colors.white,
 | 
			
		||||
            value: state.currentMode == const NativeCryptoMode(),
 | 
			
		||||
            onChanged: (_) => bloc(context).switchMode(),
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ dependencies:
 | 
			
		||||
    hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
 | 
			
		||||
    version: 2.0.0
 | 
			
		||||
  get_it: ^7.2.0
 | 
			
		||||
  file_picker: ^5.2.7
 | 
			
		||||
 | 
			
		||||
dev_dependencies:
 | 
			
		||||
  flutter_test: { sdk: flutter }
 | 
			
		||||
 | 
			
		||||
@ -8,10 +8,13 @@
 | 
			
		||||
enum Encoding {
 | 
			
		||||
  /// UTF-8 encoding, as defined by the Unicode standard.
 | 
			
		||||
  utf8,
 | 
			
		||||
 | 
			
		||||
  /// UTF-16 encoding, as defined by the Unicode standard.
 | 
			
		||||
  utf16,
 | 
			
		||||
 | 
			
		||||
  /// Base64 encoding, as defined by RFC 4648.
 | 
			
		||||
  base64,
 | 
			
		||||
 | 
			
		||||
  /// Hexadecimal encoding.
 | 
			
		||||
  base16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,10 @@
 | 
			
		||||
enum HashAlgorithm {
 | 
			
		||||
  /// The SHA-256 hash algorithm.
 | 
			
		||||
  sha256,
 | 
			
		||||
 | 
			
		||||
  /// The SHA-384 hash algorithm.
 | 
			
		||||
  sha384,
 | 
			
		||||
 | 
			
		||||
  /// The SHA-512 hash algorithm.
 | 
			
		||||
  sha512,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import 'package:native_crypto/src/domain/cipher_chunk.dart';
 | 
			
		||||
abstract class Cipher<T extends CipherChunk> {
 | 
			
		||||
  /// {@macro cipher}
 | 
			
		||||
  const Cipher();
 | 
			
		||||
 | 
			
		||||
  /// Encrypts a [Uint8List] and returns a [CipherText].
 | 
			
		||||
  Future<CipherText<T>> encrypt(Uint8List plainText);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -215,13 +215,15 @@ class MockNativeCryptoAPI implements NativeCryptoAPI {
 | 
			
		||||
    HashAlgorithm argAlgorithm,
 | 
			
		||||
  ) {
 | 
			
		||||
    if (pbkdf2Fn != null) {
 | 
			
		||||
      return Future.value(pbkdf2Fn!(
 | 
			
		||||
        argPassword,
 | 
			
		||||
        argSalt,
 | 
			
		||||
        argIterations,
 | 
			
		||||
        argLength,
 | 
			
		||||
        argAlgorithm.toString(),
 | 
			
		||||
      ),);
 | 
			
		||||
      return Future.value(
 | 
			
		||||
        pbkdf2Fn!(
 | 
			
		||||
          argPassword,
 | 
			
		||||
          argSalt,
 | 
			
		||||
          argIterations,
 | 
			
		||||
          argLength,
 | 
			
		||||
          argAlgorithm.toString(),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return Future.value(Uint8List.fromList([1, 2, 3]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ NativeCrypto - Android Implementation
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
This project is a starting point for a Flutter
 | 
			
		||||
[plug-in package](https://flutter.dev/developing-packages/),
 | 
			
		||||
a specialized package that includes platform-specific implementation code for
 | 
			
		||||
Android and/or iOS.
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
For help getting started with Flutter, view our
 | 
			
		||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
 | 
			
		||||
samples, guidance on mobile development, and a full API reference.
 | 
			
		||||
For help getting started with Flutter, view our [online documentation][3], 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 {
 | 
			
		||||
        // For now, only AES is supported
 | 
			
		||||
        val aes = AES()
 | 
			
		||||
        val params = FileParameters(context, plainTextPath, cipherTextPath)
 | 
			
		||||
        val params = FileParameters(context, cipherTextPath, plainTextPath)
 | 
			
		||||
 | 
			
		||||
        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.utils.FileParameters
 | 
			
		||||
import java.io.BufferedInputStream
 | 
			
		||||
import java.io.BufferedOutputStream
 | 
			
		||||
import javax.crypto.CipherInputStream
 | 
			
		||||
import javax.crypto.CipherOutputStream
 | 
			
		||||
import javax.crypto.spec.GCMParameterSpec
 | 
			
		||||
import javax.crypto.spec.SecretKeySpec
 | 
			
		||||
@ -60,28 +63,32 @@ class AES : Cipher {
 | 
			
		||||
            cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var len: Int?
 | 
			
		||||
        val buffer = ByteArray(8192)
 | 
			
		||||
        val inputFile = fileParameters.getFileInputStream()
 | 
			
		||||
        val outputFile = fileParameters.getFileOutputStream()
 | 
			
		||||
        val iv: ByteArray? = cipherInstance!!.iv
 | 
			
		||||
        val input = BufferedInputStream(fileParameters.getFileInputStream())
 | 
			
		||||
 | 
			
		||||
        outputFile?.write(iv)
 | 
			
		||||
        outputFile?.flush()
 | 
			
		||||
        var outputBuffered = BufferedOutputStream(fileParameters.getFileOutputStream(false))
 | 
			
		||||
        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()
 | 
			
		||||
    }
 | 
			
		||||
@ -110,9 +117,7 @@ class AES : Cipher {
 | 
			
		||||
        val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV")
 | 
			
		||||
 | 
			
		||||
        // Read the first 12 bytes from the file
 | 
			
		||||
        for (i in 0 until 12) {
 | 
			
		||||
            iv[i] = inputFile.read().toByte()
 | 
			
		||||
        }
 | 
			
		||||
        inputFile.read(iv)
 | 
			
		||||
 | 
			
		||||
        // Initialize secret key spec
 | 
			
		||||
        val sk = SecretKeySpec(key, "AES")
 | 
			
		||||
@ -125,21 +130,19 @@ class AES : Cipher {
 | 
			
		||||
 | 
			
		||||
        cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec)
 | 
			
		||||
 | 
			
		||||
        var len: Int?
 | 
			
		||||
        val buffer = ByteArray(8192)
 | 
			
		||||
        val outputFile = fileParameters.getFileOutputStream()
 | 
			
		||||
        val decryptedStream = CipherOutputStream(outputFile!!, cipherInstance)
 | 
			
		||||
        while (true) {
 | 
			
		||||
            len = inputFile.read(buffer)
 | 
			
		||||
            if(len > 0){
 | 
			
		||||
                decryptedStream.write(buffer,0, len)
 | 
			
		||||
            } else {
 | 
			
		||||
                break
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        decryptedStream.flush()
 | 
			
		||||
        decryptedStream.close()
 | 
			
		||||
        inputFile.close()
 | 
			
		||||
        val input = CipherInputStream(BufferedInputStream(inputFile), cipherInstance)
 | 
			
		||||
        val output = BufferedOutputStream(fileParameters.getFileOutputStream(false))
 | 
			
		||||
 | 
			
		||||
        var i: Int
 | 
			
		||||
        do {
 | 
			
		||||
            i = input.read()
 | 
			
		||||
            if (i != -1) output.write(i)
 | 
			
		||||
        } while (i != -1)
 | 
			
		||||
 | 
			
		||||
        output.flush()
 | 
			
		||||
        output.close()
 | 
			
		||||
 | 
			
		||||
        input.close()
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        return try{
 | 
			
		||||
            FileOutputStream(path)
 | 
			
		||||
            FileOutputStream(path, append)
 | 
			
		||||
        } catch(e: IOException){
 | 
			
		||||
            val documentFile: DocumentFile = this.getDocumentFileByPath(path)
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,13 @@
 | 
			
		||||
# NativeCrypto - iOS Implementation
 | 
			
		||||
 | 
			
		||||
iOS Implementation of NativeCrypto Plugin.
 | 
			
		||||
iOS Implementation of [NativeCrypto][1] Plugin.
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
This project is a starting point for a Flutter
 | 
			
		||||
[plug-in package](https://flutter.dev/developing-packages/),
 | 
			
		||||
a specialized package that includes platform-specific implementation code for
 | 
			
		||||
Android and/or iOS.
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
For help getting started with Flutter, view our
 | 
			
		||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
 | 
			
		||||
samples, guidance on mobile development, and a full API reference.
 | 
			
		||||
For help getting started with Flutter, view our [online documentation][3], 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 .sha384: return FlutterStandardTypedData(bytes: Data(HMAC<SHA384>(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 .sha384: return SHA384.init()
 | 
			
		||||
        case .sha512: return SHA512.init()
 | 
			
		||||
        @unknown default: fatalError("Unknown algorithm")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@ -24,7 +23,6 @@ public class HashAlgorithmParser {
 | 
			
		||||
        case .sha256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256)
 | 
			
		||||
        case .sha384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384)
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
 | 
			
		||||
[1]: ../native_crypto
 | 
			
		||||
[1]: ../../README.md
 | 
			
		||||
[2]: lib/native_crypto_platform_interface.dart
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user