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])); }