The goal of this plugin is to provide a fast and powerful cryptographic functions by calling native libraries. On Android, it uses javax.cypto, and on iOS, it uses CommonCrypto and 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 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.
- 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
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 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.
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)
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.
To digest a message, you'll need to initialize a Hasher object implementing Hash
. Then, you can digest your 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
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.
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
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.
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 = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]);
SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes
You can derive a SecretKey
using PBKDF2.
First, you need to initialize a Pbkdf2
object.
final Pbkdf2 pbkdf2 = Pbkdf2(
length: 32, // 32 bytes
iterations: 1000,
salt: salt.toBytes(),
hashAlgorithm: HashAlgorithm.sha256,
);
Then, you can derive a SecretKey
from a password.
SecretKey secretKey = await pbkdf2(password: password);
Note: Pbkdf2 is a callable class. You can use it like a function.
And now, you can use the SecretKey
to encrypt/decrypt a message.
First, you need to initialize a Cipher
object.
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
Then, you can encrypt your message.
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
After an encryption you obtain a CipherText
which contains chunks. You can get the underlying bytes with cipherText.bytes
.
Uppon receiving encrypted message receivedData
, you can decrypt it.
You have to reconstruct the ciphertext and the setup the chunk factory.
final CipherText<AESCipherChunk> receivedCipherText CipherText(
receivedData,
chunkFactory: (bytes) => AESCipherChunk(
bytes,
ivLength: cipher.mode.ivLength,
tagLength: cipher.mode.tagLength,
),
),
Then, you can decrypt your message.
Uint8List message = await cipher.decrypt(receivedCipherText);
You can encrypt/decrypt files.
First, you need to initialize a Cipher
object.
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
Then, you can encrypt your file.
await cipher.encryptFile(plainText, cipherText);
Note:
plainText
andcipherText
areFile
objects.
You can decrypt your file.
await cipher.decryptFile(cipherText, plainText);
You can force the use of a specific IV. Please note that the IV must be unique for each encryption.
final CipherText<AESCipherChunk> cipherText = await cipher.encryptWithIV(message, iv);
encrypt(...)
instead of encryptWithIV(...)
if you don't know what you are doing.
- Launch Android Studio.
- Select Open an existing Android Studio Project in the Welcome to Android Studio dialog, or select File > Open from the menu, and select the
packages/native_crypto/example/android/build.gradle
file. - In the Gradle Sync dialog, select OK.
- In the Android Gradle Plugin Update dialog, select Don’t remind me again for this project.
- Launch Xcode.
- Select File > Open, and select the
packages/native_crypto/example/ios/Runner.xcworkspace
file.