diff --git a/README.md b/README.md
index 6ec789e..ed6621c 100644
--- a/README.md
+++ b/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.
- */
Fast and powerful cryptographic functions for Flutter.
@@ -42,6 +35,71 @@ 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 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 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
First, check compatibility with your targets.
@@ -50,26 +108,42 @@ First, check compatibility with your targets.
| --- | ------- | ----- | ----- | ------- | --- |
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
+> 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))
+
#### 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 +153,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 +176,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();
-// same as
-CipherText cipherText = wrapper.single;
-
-// or
-
-List cipherTexts = wrapper.unwrap>();
-// same as
-List cipherTexts = wrapper.list;
+final CipherText cipherText = await cipher.encrypt(message);
```
-After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List` 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 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 cipherText = await cipher.encryptWithIV(message, iv);
+```
+
+⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing.
+
## Development
### Android
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
index 7bc8f26..72e81d7 100644
--- a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
@@ -1,5 +1,5 @@
// 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.
diff --git a/packages/native_crypto/lib/src/core/constants/constants.dart b/packages/native_crypto/lib/src/core/constants/constants.dart
index 97b111c..37fe0b5 100644
--- a/packages/native_crypto/lib/src/core/constants/constants.dart
+++ b/packages/native_crypto/lib/src/core/constants/constants.dart
@@ -6,7 +6,7 @@
abstract class Constants {
/// The default chunk size in bytes used for encryption and decryption.
- ///
+ ///
/// ~32MB
static const int defaultChunkSize = 33554432;
diff --git a/packages/native_crypto/lib/src/core/enums/encoding.dart b/packages/native_crypto/lib/src/core/enums/encoding.dart
index 2b69508..fb459dd 100644
--- a/packages/native_crypto/lib/src/core/enums/encoding.dart
+++ b/packages/native_crypto/lib/src/core/enums/encoding.dart
@@ -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,
}
diff --git a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart
index 7748efb..860f609 100644
--- a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart
+++ b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart
@@ -8,8 +8,10 @@
enum HashAlgorithm {
/// The SHA-256 hash algorithm.
sha256,
+
/// The SHA-384 hash algorithm.
sha384,
+
/// The SHA-512 hash algorithm.
sha512,
}
diff --git a/packages/native_crypto/lib/src/core/extensions/extensions.dart b/packages/native_crypto/lib/src/core/extensions/extensions.dart
index f10b73b..811436f 100644
--- a/packages/native_crypto/lib/src/core/extensions/extensions.dart
+++ b/packages/native_crypto/lib/src/core/extensions/extensions.dart
@@ -1,5 +1,5 @@
// 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.
diff --git a/packages/native_crypto/lib/src/core/utils/utils.dart b/packages/native_crypto/lib/src/core/utils/utils.dart
index a673ed2..40b6a15 100644
--- a/packages/native_crypto/lib/src/core/utils/utils.dart
+++ b/packages/native_crypto/lib/src/core/utils/utils.dart
@@ -1,5 +1,5 @@
// 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.
diff --git a/packages/native_crypto/lib/src/domain/base_key.dart b/packages/native_crypto/lib/src/domain/base_key.dart
index 2dc92cb..be6e135 100644
--- a/packages/native_crypto/lib/src/domain/base_key.dart
+++ b/packages/native_crypto/lib/src/domain/base_key.dart
@@ -12,7 +12,7 @@ import 'package:native_crypto/src/domain/byte_array.dart';
/// [BaseKey] is a [ByteArray] that can be used to store keys.
///
/// This interface is implemented by all the key classes.
-///
+///
/// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with
/// the Key class from Flutter.
/// {@endtemplate}
diff --git a/packages/native_crypto/lib/src/domain/cipher.dart b/packages/native_crypto/lib/src/domain/cipher.dart
index 6ad3ae1..ee9a475 100644
--- a/packages/native_crypto/lib/src/domain/cipher.dart
+++ b/packages/native_crypto/lib/src/domain/cipher.dart
@@ -16,6 +16,7 @@ import 'package:native_crypto/src/domain/cipher_chunk.dart';
abstract class Cipher {
/// {@macro cipher}
const Cipher();
+
/// Encrypts a [Uint8List] and returns a [CipherText].
Future> encrypt(Uint8List plainText);
diff --git a/packages/native_crypto/lib/src/domain/domain.dart b/packages/native_crypto/lib/src/domain/domain.dart
index 58cc8b7..a22b2cc 100644
--- a/packages/native_crypto/lib/src/domain/domain.dart
+++ b/packages/native_crypto/lib/src/domain/domain.dart
@@ -1,5 +1,5 @@
// 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.
diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart
index d2e1831..a1b2e1b 100644
--- a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart
+++ b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart
@@ -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]));
}