Compare commits

...

6 Commits

42 changed files with 1061 additions and 319 deletions

202
README.md
View File

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

View File

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

View File

@ -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:
![](resources/benchmarks.png)
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).

View File

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

View File

@ -47,5 +47,9 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UISupportsDocumentBrowser</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
</dict>
</plist>

View File

@ -4,6 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:native_crypto/native_crypto.dart';
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
@ -30,6 +32,29 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
return plainText;
}
@override
Future<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
) async {
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
final plainText = File.fromUri(
Uri.parse(
'${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}',
),
);
await cipher.decryptFile(
cipherText,
plainText,
);
}
@override
Future<SecretKey> deriveKeyFromPassword(
String password, {
@ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource {
return cipherText.bytes;
}
@override
Future<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
) async {
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
final cipherText = File.fromUri(
Uri.parse(
'${folderResult.path}/${plainText.path.split('/').last}.enc',
),
);
await cipher.encryptFile(plainText, cipherText);
}
@override
Future<SecretKey> generateSecureRandom(int length) async {
final SecretKey sk = await SecretKey.fromSecureRandom(length);

View File

@ -6,6 +6,8 @@
// ignore_for_file: implementation_imports
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:native_crypto/native_crypto.dart';
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
@ -37,6 +39,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
return paddedPlainText;
}
@override
Future<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
) async {
throw UnimplementedError();
}
@override
Future<SecretKey> deriveKeyFromPassword(
String password, {
@ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
@override
Future<Uint8List> encryptWithIV(
Uint8List data, SecretKey key, Uint8List iv,) async {
Uint8List data,
SecretKey key,
Uint8List iv,
) async {
final gcm = GCMBlockCipher(AESEngine())
..init(
true,
@ -144,6 +158,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource {
);
}
@override
Future<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
) async {
throw UnimplementedError();
}
@override
Future<SecretKey> generateSecureRandom(int length) async {
if (_secureRandom == null) {

View File

@ -4,6 +4,7 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:io';
import 'dart:typed_data';
import 'package:native_crypto/native_crypto.dart';
@ -30,6 +31,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
},
);
@override
FutureOrResult<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<SecretKey> deriveKeyFromPassword(
String password, {
@ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository {
@override
FutureOrResult<Uint8List> encryptWithIV(
Uint8List data, SecretKey key, Uint8List iv,) =>
Uint8List data,
SecretKey key,
Uint8List iv,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.encryptWithIV(data, key, iv),
(error) {
@ -97,6 +117,22 @@ class CryptoRepositoryImpl extends CryptoRepository {
},
);
@override
FutureOrResult<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.encryptFile(plainText, folderResult, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<SecretKey> generateSecureRandom(int length) =>
Result.tryCatchAsync(

View File

@ -0,0 +1,167 @@
// Copyright 2019-2023 Hugo Pointcheval
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:io';
import 'dart:typed_data';
import 'package:native_crypto/native_crypto.dart';
import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
import 'package:native_crypto_example/domain/entities/mode.dart';
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class CryptoRepositorySwitchableImpl extends CryptoRepository {
CryptoRepositorySwitchableImpl({
required this.nativeCryptoDataSource,
required this.pointyCastleDataSource,
required this.currentMode,
});
CryptoDataSource nativeCryptoDataSource;
CryptoDataSource pointyCastleDataSource;
Mode currentMode;
set mode(Mode mode) {
currentMode = mode;
}
CryptoDataSource get cryptoDataSource {
if (currentMode is NativeCryptoMode) {
return nativeCryptoDataSource;
} else if (currentMode is PointyCastleMode) {
return pointyCastleDataSource;
} else {
throw Exception('Unknown mode');
}
}
@override
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key) =>
Result.tryCatchAsync(
() async => cryptoDataSource.decrypt(data, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<SecretKey> deriveKeyFromPassword(
String password, {
required String salt,
}) =>
Result.tryCatchAsync(
() async => cryptoDataSource.deriveKeyFromPassword(
password,
salt: salt,
),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data) =>
Result.tryCatchAsync(
() async => cryptoDataSource.hash(hasher, data),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key) =>
Result.tryCatchAsync(
() async => cryptoDataSource.hmac(hmac, data, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key) =>
Result.tryCatchAsync(
() async => cryptoDataSource.encrypt(data, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<Uint8List> encryptWithIV(
Uint8List data,
SecretKey key,
Uint8List iv,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.encryptWithIV(data, key, iv),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
) =>
Result.tryCatchAsync(
() async => cryptoDataSource.encryptFile(plainText, folderResult, key),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
@override
FutureOrResult<SecretKey> generateSecureRandom(int length) =>
Result.tryCatchAsync(
() async => cryptoDataSource.generateSecureRandom(length),
(error) {
if (error is NativeCryptoException) {
return ClientException('${error.message}');
}
return ClientException(error.toString());
},
);
}

View File

@ -4,6 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:native_crypto/native_crypto.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@ -15,12 +17,22 @@ abstract class CryptoDataSource extends BaseDataSource {
required String salt,
});
Future<Uint8List> encrypt(Uint8List data, SecretKey key);
Future<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
);
Future<Uint8List> encryptWithIV(
Uint8List data,
SecretKey key,
Uint8List iv,
);
Future<Uint8List> decrypt(Uint8List data, SecretKey key);
Future<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
);
Future<Uint8List> hash(Hash hasher, Uint8List data);
Future<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);
}

View File

@ -4,6 +4,8 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:native_crypto/native_crypto.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@ -15,12 +17,22 @@ abstract class CryptoRepository extends BaseRepository {
required String salt,
});
FutureOrResult<Uint8List> encrypt(Uint8List data, SecretKey key);
FutureOrResult<void> encryptFile(
File plainText,
Uri folderResult,
SecretKey key,
);
FutureOrResult<Uint8List> encryptWithIV(
Uint8List data,
SecretKey key,
Uint8List iv,
);
FutureOrResult<Uint8List> decrypt(Uint8List data, SecretKey key);
FutureOrResult<void> decryptFile(
File cipherText,
Uri folderResult,
SecretKey key,
);
FutureOrResult<Uint8List> hash(Hash hasher, Uint8List data);
FutureOrResult<Uint8List> hmac(Hmac hmac, Uint8List data, SecretKey key);

View File

@ -8,9 +8,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:native_crypto_example/core/get_it.dart';
import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart';
import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart';
import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart';
import 'package:native_crypto_example/data/repositories/session_repository_impl.dart';
import 'package:native_crypto_example/domain/entities/mode.dart';
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
@ -28,6 +30,12 @@ class App extends StatelessWidget {
final SessionRepository _sessionRepository =
SessionRepositoryImpl(sessionDataSource: getIt());
final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl(
nativeCryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
pointyCastleDataSource: getIt<PointyCastleDataSourceImpl>(),
currentMode: const NativeCryptoMode(),
);
@override
Widget build(BuildContext context) => MultiProvider(
repositoryProviders: [
@ -35,18 +43,17 @@ class App extends StatelessWidget {
RepositoryProvider<SessionRepository>.value(
value: _sessionRepository,
),
RepositoryProvider<CryptoRepository>(
create: (_) => CryptoRepositoryImpl(
cryptoDataSource: getIt<NativeCryptoDataSourceImpl>(),
),
),
RepositoryProvider<CryptoRepository>.value(value: _cryptoRepository),
],
blocProviders: [
BlocProvider<OutputCubit>(
create: (_) => OutputCubit(_loggerRepository),
),
BlocProvider<ModeSwitcherCubit>(
create: (_) => ModeSwitcherCubit(_sessionRepository),
create: (_) => ModeSwitcherCubit(
_sessionRepository,
_cryptoRepository,
),
)
],
child: MaterialApp(

View File

@ -0,0 +1,132 @@
// Copyright 2019-2023 Hugo Pointcheval
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:native_crypto_example/domain/entities/log_message.dart';
import 'package:native_crypto_example/domain/entities/states.dart';
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
part 'benchmark_state.dart';
class BenchmarkCubit extends Cubit<BenchmarkState> {
BenchmarkCubit({
required this.sessionRepository,
required this.loggerRepository,
required this.cryptoRepository,
}) : super(const BenchmarkState.initial());
final SessionRepository sessionRepository;
final LoggerRepository loggerRepository;
final CryptoRepository cryptoRepository;
List<int> testedSizes = [
2097152,
6291456,
10485760,
14680064,
18874368,
23068672,
27262976,
31457280,
35651584,
39845888,
44040192,
48234496,
52428800,
];
FutureOr<void> launchBenchmark() async {
emit(const BenchmarkState.loading());
final sk = await sessionRepository.getSessionKey();
if (sk.isErr) {
await loggerRepository.addLog(
const LogError('No SecretKey!\n'
'Go in Key tab and generate or derive one.'),
);
emit(
BenchmarkState.failure(
sk.err?.message,
),
);
}
int run = 0;
final csv = StringBuffer(
'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n',
);
for (final size in testedSizes) {
run++;
final StringBuffer csvLine = StringBuffer();
final dummyBytes = Uint8List(size);
csvLine.write('$run;$size;');
// Encryption
final beforeEncryption = DateTime.now();
final encryptedBigFileResult = await cryptoRepository.encrypt(
dummyBytes,
sk.ok!,
);
final afterEncryption = DateTime.now();
final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch -
beforeEncryption.millisecondsSinceEpoch;
await loggerRepository.addLog(
LogInfo(
'[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms',
),
);
csvLine.write('$benchmarkEncryption');
if (encryptedBigFileResult.isErr) {
await loggerRepository.addLog(
LogError(
'Encryption failed: ${encryptedBigFileResult.err?.message}',
),
);
emit(
BenchmarkState.failure(
encryptedBigFileResult.err?.message,
),
);
return;
}
// Decryption
final beforeDecryption = DateTime.now();
await cryptoRepository.decrypt(
encryptedBigFileResult.ok!,
sk.ok!,
);
final afterDecryption = DateTime.now();
final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch -
beforeDecryption.millisecondsSinceEpoch;
await loggerRepository.addLog(
LogInfo(
'[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms',
),
);
csvLine.write(';$benchmarkDecryption');
csv.writeln(csvLine);
}
debugPrint(csv.toString());
emit(
const BenchmarkState.success(),
);
return;
}
}

View File

@ -0,0 +1,27 @@
// Copyright 2019-2023 Hugo Pointcheval
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
part of 'benchmark_cubit.dart';
@immutable
class BenchmarkState {
const BenchmarkState.initial()
: state = State.initial,
error = null;
const BenchmarkState.loading()
: state = State.loading,
error = null;
const BenchmarkState.failure(this.error) : state = State.failure;
const BenchmarkState.success()
: state = State.success,
error = null;
final State state;
final String? error;
}

View File

@ -0,0 +1,55 @@
// Copyright 2019-2023 Hugo Pointcheval
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'package:flutter/material.dart';
import 'package:native_crypto_example/core/typography.dart';
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart';
import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
class BenchmarkStateManagement
extends CubitScreen<BenchmarkCubit, BenchmarkState> {
const BenchmarkStateManagement({super.key});
@override
BenchmarkCubit create(BuildContext context) => BenchmarkCubit(
sessionRepository: repo<SessionRepository>(context),
loggerRepository: repo<LoggerRepository>(context),
cryptoRepository: repo<CryptoRepository>(context),
);
@override
Widget onBuild(BuildContext context, BenchmarkState state) => ListView(
children: [
const Logs(),
const Padding(
padding: EdgeInsets.all(8),
child: Text(
'Benchmark',
style: AppTypography.title,
),
),
const Padding(
padding: EdgeInsets.all(8),
child: Text(
'''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''',
style: AppTypography.body,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: ButtonStateManagement(
label: 'Launch',
onPressed: () => bloc(context).launchBenchmark(),
),
),
],
);
}

View File

@ -5,7 +5,9 @@
// https://opensource.org/licenses/MIT.
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:native_crypto/native_crypto.dart';
@ -232,4 +234,184 @@ class AESCubit extends Cubit<AESState> {
return;
}
FutureOr<void> encryptFile() async {
emit(state.copyWith(state: State.loading));
final sk = await sessionRepository.getSessionKey();
if (sk.isErr) {
await loggerRepository.addLog(
const LogError('No SecretKey!\n'
'Go in Key tab and generate or derive one.'),
);
emit(
state.copyWith(
state: State.failure,
error: sk.err?.message,
),
);
return;
}
// Pick file to encrypt
final pickFileResult = await FilePicker.platform.pickFiles();
if (pickFileResult == null) {
await loggerRepository.addLog(
const LogError('No file selected.'),
);
emit(
state.copyWith(
state: State.failure,
error: 'No file selected.',
),
);
return;
}
final file = File(pickFileResult.files.single.path!);
// Pick folder to store the encrypted file
final resultFolder = await FilePicker.platform.getDirectoryPath();
if (resultFolder == null) {
await loggerRepository.addLog(
const LogError('No folder selected.'),
);
emit(
state.copyWith(
state: State.failure,
error: 'No folder selected.',
),
);
return;
}
final folder = Directory(resultFolder);
final encryption = await cryptoRepository.encryptFile(
file,
folder.uri,
sk.ok!,
);
emit(
await encryption.foldAsync(
(_) async {
await loggerRepository.addLog(
const LogInfo('File successfully encrypted.\n'),
);
return state.copyWith(
state: State.success,
plainTextFile: '',
cipherTextFile: '',
);
},
(error) async {
await loggerRepository.addLog(
LogError(error.message ?? 'Error during encryption.'),
);
return state.copyWith(
state: State.failure,
error: error.message,
);
},
),
);
return;
}
FutureOr<void> decryptFile() async {
emit(state.copyWith(state: State.loading));
final sk = await sessionRepository.getSessionKey();
if (sk.isErr) {
await loggerRepository.addLog(
const LogError('No SecretKey!\n'
'Go in Key tab and generate or derive one.'),
);
emit(
state.copyWith(
state: State.failure,
error: sk.err?.message,
),
);
return;
}
await FilePicker.platform.clearTemporaryFiles();
final resultPickFile = await FilePicker.platform.pickFiles();
if (resultPickFile == null) {
await loggerRepository.addLog(
const LogError('No file selected.'),
);
emit(
state.copyWith(
state: State.failure,
error: 'No file selected.',
),
);
return;
}
final file = File(resultPickFile.files.single.path!);
// Pick folder to store the encrypted file
final resultFolder = await FilePicker.platform.getDirectoryPath();
if (resultFolder == null) {
await loggerRepository.addLog(
const LogError('No folder selected.'),
);
emit(
state.copyWith(
state: State.failure,
error: 'No folder selected.',
),
);
return;
}
final folder = Directory(resultFolder);
final decryption =
await cryptoRepository.decryptFile(file, folder.uri, sk.ok!);
emit(
await decryption.foldAsync(
(_) async {
await loggerRepository.addLog(
const LogInfo('File successfully decrypted.\n'),
);
return state.copyWith(
state: State.success,
plainTextFile: '',
cipherTextFile: '',
);
},
(error) async {
await loggerRepository.addLog(
LogError(error.message ?? 'Error during decryption.'),
);
return state.copyWith(
state: State.failure,
error: error.message,
);
},
),
);
return;
}
}

View File

@ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen<AESCubit, AESState> {
onPressed: () => bloc(context).decryptFromMemory(),
),
),
const Padding(
padding: EdgeInsets.all(8),
child: Text(
'File',
style: AppTypography.title,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: ButtonStateManagement(
label: 'Encrypt file',
onPressed: () => bloc(context).encryptFile(),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: ButtonStateManagement(
label: 'Decrypt file',
onPressed: () => bloc(context).decryptFile(),
),
),
const Padding(
padding: EdgeInsets.all(8),
child: Text(

View File

@ -8,7 +8,9 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
import 'package:native_crypto_example/domain/entities/mode.dart';
import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
import 'package:native_crypto_example/domain/repositories/session_repository.dart';
part 'mode_switcher_state.dart';
@ -16,8 +18,11 @@ part 'mode_switcher_state.dart';
class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
ModeSwitcherCubit(
this.sessionRepository,
this.cryptoRepository,
) : super(const ModeSwitcherState(NativeCryptoMode()));
SessionRepository sessionRepository;
CryptoRepository cryptoRepository;
FutureOr<void> switchMode() async {
final currentMode = await sessionRepository.getCurrentMode();
@ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit<ModeSwitcherState> {
}
sessionRepository.setCurrentMode(newMode);
if (cryptoRepository is CryptoRepositorySwitchableImpl) {
(cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
}
} else {
newMode = const NativeCryptoMode();
sessionRepository.setCurrentMode(newMode);
if (cryptoRepository is CryptoRepositorySwitchableImpl) {
(cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
}
}
emit(ModeSwitcherState(newMode));

View File

@ -5,12 +5,12 @@
// https://opensource.org/licenses/MIT.
import 'package:flutter/material.dart';
import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart';
import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart';
import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart';
import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart';
import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart';
import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart';
import 'package:native_crypto_example/presentation/home/widgets/blank.dart';
import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart';
import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
@ -24,7 +24,7 @@ class HomeStateManagement
HashStateManagement(),
AESStateManagement(),
TestVectorsStateManagement(),
const Blank()
const BenchmarkStateManagement(),
];
@override

View File

@ -22,12 +22,12 @@ class AppBarStateManagement
: 'PointyCastle',
),
backgroundColor: state.currentMode.primaryColor,
// TODO(hpcl): enable mode switcher
// actions: [
// Switch(
// value: state.currentMode == const NativeCryptoMode(),
// onChanged: (_) => bloc(context).switchMode(),
// )
// ],
actions: [
Switch(
activeColor: Colors.white,
value: state.currentMode == const NativeCryptoMode(),
onChanged: (_) => bloc(context).switchMode(),
)
],
);
}

View File

@ -32,6 +32,7 @@ dependencies:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 2.0.0
get_it: ^7.2.0
file_picker: ^5.2.7
dev_dependencies:
flutter_test: { sdk: flutter }

View File

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

View File

@ -8,8 +8,10 @@
enum HashAlgorithm {
/// The SHA-256 hash algorithm.
sha256,
/// The SHA-384 hash algorithm.
sha384,
/// The SHA-512 hash algorithm.
sha512,
}

View File

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

View File

@ -215,13 +215,15 @@ class MockNativeCryptoAPI implements NativeCryptoAPI {
HashAlgorithm argAlgorithm,
) {
if (pbkdf2Fn != null) {
return Future.value(pbkdf2Fn!(
return Future.value(
pbkdf2Fn!(
argPassword,
argSalt,
argIterations,
argLength,
argAlgorithm.toString(),
),);
),
);
} else {
return Future.value(Uint8List.fromList([1, 2, 3]));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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