Overview
Popular cryptographic algorithms for Dart / Flutter developers.
Package was maintained by gohilla.com. Repository was moved to emz-hanauer/dart-cryptography due to lack of maintenance.
Licensed under the Apache License 2.0.
This package is designed to be:
- Easy to use. The API is easy to understand and encourages good defaults.
- Multi-platform. It's easy to customize implementation of X in platform Y.
- Fast. We use platform APIs when available. For example, SHA-512 is over 100 times faster than package:crypto in browsers.
Any feedback, issue reports, or pull requests are appreciated!
Links
Getting started
If you use Flutter, it's recommended (but not necessarily) that you also import our sibling package cryptography_flutter_plus, which delegates calls to Android / iOS / Mac OS X operating system APIs whenever possible.
In pubspec.yaml:
dependencies:
cryptography_plus: ^2.7.0
cryptography_flutter_plus: ^2.3.2 # Remove if you don't use Flutter
You are ready to go!
Issues and discussions
Please report bugs at github.com/emz-hanauer/dart-cryptography/issues.
Having questions? Feel free to use our Github Discussions at github.com/emz-hanauer/dart-cryptography/discussions.
Some things to know
General guidance on cryptography
Please read information about Mobile App Cryptography by OWASP. It contains a lot of useful information that helps you build more secure software.
Common parameters
- SecretKey
- Used by ciphers, message authentication codes, and key derivation functions.
- KeyPair
- SimpleKeyPair (Octet sequences such as Ed25519 / X25519 32-byte private keys)
- EcKeyPairData (P-256, P-384, P-521 private keys)
- RsaKeyPairData (RSA private keys)
- PublicKey
- SimplePublicKey (Octet sequences such as Ed25519 / X25519 32-byte public keys)
- EcPublicKey (P-256 / P-384 / P-512 public keys)
- RsaPublicKey (RSA public keys)
Note that SecretKey and KeyPair instances are opaque and asynchronous by default. They may not be in the memory and may not be readable at all. If a SecretKey or KeyPair instance is in memory, it's an instance of one of the following:
For encoding/decoding private/public keys in JWK (JSON Web Key) format, use package:jwk. For encoding/decoding X.509, PKCS12, and other formats, we don't have recommended packages at the moment.
Wands (recommended)
For a bit higher API abstraction and sometimes a performance boost, we encourage developers to use Wand instances which do operations with fixed, "invisible" key (thus "magic wands"). There are currently three types of wands:
In the future version 3.x, we plan to transition to wands as the default API for doing operations.
Ciphers
The following Cipher implementations are available:
- AES
- AesCbc (
AES-CBC)
- The throughput is up to 120 MB / second (Macbook Pro, message size 1 MB).
- AesCtr (
AES-CTR)
- The throughput is up to 80 MB / second (Macbook Pro, message size 1 MB).
- AesGcm (
AES-GCM)
- The throughput is up to 20 MB / second (Macbook Pro, message size 1 MB).
- In browsers, the package uses Web Cryptography, reaching over 500 MB/s.
- AesCbc (
AES-CBC)
- ChaCha20 / XChaCha20
- Chacha20
- The throughput is up to 230 MB / second (Macbook Pro, message size 1 MB).
- Chacha20.poly1305Aead (
AEAD_CHACHA20_POLY1305)
- The throughput is up to 30 MB / second (Macbook Pro, message size 1 MB).
- Xchacha20
- Xchacha20.poly1305Aead ( AEAD_XCHACHA20_POLY1305)
- Chacha20
Digital signature algorithms
The following SignatureAlgorithm implementations are available:
- Ed25519 (
curve25519 EdDSA)
- Performance of the pure Dart implementation is around 200 (signatures or verifications) per second in VM and about 50 in browsers.
- Elliptic curves approved by NIST
- Ecdsa.p256 ( ECDSA P256 / secp256r1 / prime256v1 + SHA256)
- Ecdsa.p384 ( ECDSA P384 / secp384r1 / prime384v1 + SHA384)
- Ecdsa.p521 ( ECDSA P521 / secp521r1 / prime521v1 + SHA256)
- We don't have implementations of these in pure Dart.
- RSA
- RsaPss ( RSA-PSS)
- RsaSsaPkcs1v15 ( RSASSA-PKCS1v15)
- We don't have implementations of these in pure Dart.
Key exchange algorithms
The following KeyExchangeAlgorithm implementations are available:
- Elliptic curves approved by NIST
- X25519 (
curve25519 Diffie-Hellman)
- Throughput of the pure Dart implementation is around 1000 key agreements per second (in VM).
Key derivation / password hashing algorithms
The following KdfAlgorithm implementations are available:
Message authentication codes
The following MacAlgorithm implementations are available:
Cryptographic hash functions
The following HashAlgorithm implementations are available:
- Blake2b (BLAKE2B)
- Blake2s (BLAKE2S)
- Sha1 (SHA1)
- Sha224 (SHA2-224)
- Sha256 (SHA2-256)
- Sha384 (SHA2-384)
- Sha512 (SHA2-512)
The performance is excellent! Our pure Dart implementation of Sha256 is around 2 times faster than implementation in the older "package:crypto" because of inlining and smarter integer use. If you do lots of small hashes, the advantage is even bigger because this package allows you re-use the hash state. Therefore our HMAC-SHA256 is much faster too. In browsers, hashes can be over 100 times faster because this package automatically uses Web Crypto API.
Random number generators
We use Dart SDK Random.secure() as the default random number in all APIs.
If you do want much faster cryptographically reasonably strong random numbers, this package contains SecureRandom.fast. It generates random numbers by using 12 round version ChaCha cipher. It can generate up to 0.25 GB random data per second because it makes expensive operating system call less frequently after the initial seeding from the operating system. While ChaCha is not designed to be a cryptographic random number generator, it can be acceptable and a related (Blake2-based) RNG is used by Linux kernel.
If you need a deterministic random number generator for tests, the package contains SecureRandom.forTesting, which uses the aforementioned ChaCha-based RNG with a fixed seed and no re-seeding.
Cryptographic factory class
The class Cryptography has factory methods that return implementations of cryptographic algorithms. The default implementation is BrowserCryptography (which works in all platforms, not just browser).
We wrote the following three implementations of Cryptography
:
- DartCryptography
- Gives you implementations written in pure Dart implementations. They work in all platforms.
- The algorithms are written and tested by us. They are are licensed under the Apache 2.0 License.
- See the class documentation for list algorithms supported by it.
- BrowserCryptography
- Uses Web Cryptography API (crypto.subtle) whenever possible. Methods return pure Dart implementations when Web Cryptography API is not available.
- See the class documentation for list algorithms supported by it.
- FlutterCryptography
- Makes cryptographic algorithms up to 100 times faster in Android, iOS, and Mac OS X.
- Available in the package cryptography_flutter.
- See the class documentation for list algorithms supported by it.
Deterministic behavior in tests
If you want to have deterministic behavior in tests, you can supply your own random number generator:
For example:
import 'package:cryptography_plus/cryptography_plus.dart';
void main() {
setUp(() {
Cryptography.instance = Cryptography.instance.withRandom(SecureRandom.forTesting(seed: 42));
});
test('example', () async {
final algorithm = AesGcm.with256bits();
// This will will always return the same secret key.
// because we use a deterministic random number generator.
final secretKey = await algorithm.newSecretKey();
// ...
});
}
Examples
Digital signature
In this example, we use Ed25519, a popular signature algorithm:
import 'package:cryptography_plus/cryptography_plus.dart';
Future<void> main() async {
// The message that we will sign
final message = <int>[1, 2, 3];
// Generate a keypair.
final algorithm = Ed25519();
final keyPair = await algorithm.newKeyPair();
// Sign
final signature = await algorithm.sign(
message,
keyPair: keyPair,
);
print('Signature: ${signature.bytes}');
print('Public key: ${signature.publicKey.bytes}');
// Verify signature
final isSignatureCorrect = await algorithm.verify(
message,
signature: signature,
);
print('Correct signature: $isSignatureCorrect');
}
Key agreement
In this example, we use X25519, a popular key agreement algorithm:
import 'package:cryptography_plus/cryptography_plus.dart';
Future<void> main() async {
final algorithm = X25519();
// Alice chooses her key pair
final aliceKeyPair = await algorithm.newKeyPair();
// Alice knows Bob's public key
final bobKeyPair = await algorithm.newKeyPair();
final bobPublicKey = await bobKeyPair.extractPublicKey();
// Alice calculates the shared secret.
final sharedSecret = await algorithm.sharedSecretKey(
keyPair: aliceKeyPair,
remotePublicKey: bobPublicKey,
);
final sharedSecretBytes = await sharedSecret.extractBytes();
print('Shared secret: $sharedSecretBytes');
}
Authenticated encryption
When you encrypt, you need:
- Clear text (bytes you are encrypting)
- SecretKey. You can generate a random secret key with cipher.newSecretKey().
- An optional nonce (also known as "IV", "Initialization Vector", "salt"), which is some non-secret byte sequence that MUST be unique each time you encrypt. If you don't provide a nonce, a random nonce will be automatically generated for you.
The output of encrypting is SecretBox that contains the nonce, the cipher text (the encrypted message), and Mac (a message authentication code).
When you decrypt, you need the SecretBox and the secret key.
In the following example, we encrypt a message with AesGcm:
import 'dart:convert';
import 'package:cryptography_plus/cryptography_plus.dart';
Future<void> main() async {
// Choose the cipher
final algorithm = AesGcm.with256bits();
// Generate a random secret key.
final secretKey = await algorithm.newSecretKey();
final secretKeyBytes = await secretKey.extractBytes();
print('Secret key: ${secretKeyBytes}');
// Encrypt
final secretBox = await algorithm.encryptString(
'Hello!',
secretKey: secretKey,
);
print('Nonce: ${secretBox.nonce}'); // Randomly generated nonce
print('Ciphertext: ${secretBox.cipherText}'); // Encrypted message
print('MAC: ${secretBox.mac}'); // Message authentication code
// If you are sending the secretBox somewhere, you can concatenate all parts of it:
final concatenatedBytes = secretBox.concatenation();
print('All three parts concatenated: $concatenatedBytes');
// Decrypt
final clearText = await algorithm.decryptString(
secretBox,
secretKey: secretKey,
);
print('Cleartext: $clearText'); // Hello!
}
Password hashing
In this example, we use Argon2id:
import 'package:cryptography_plus/cryptography_plus.dart';
Future<void> main() async {
final algorithm = Argon2id(
memory: 10*1000, // 10 MB
parallelism: 2, // Use maximum two CPU cores.
iterations: 1, // For more security, you should usually raise memory parameter, not iterations.
hashLength: 32, // Number of bytes in the returned hash
);
final secretKey = await algorithm.deriveKeyFromPassword(
password: 'qwerty',
nonce: [1, 2, 3],
);
final secretKeyBytes = await secretKey.extractBytes();
print('Hash: ${secretKeyBytes}');
}
Hashing
In this example, we use Sha256:
import 'package:cryptography_plus/cryptography_plus.dart';
Future<void> main() async {
final sink = Sha256().newHashSink();
// Add all parts of the authenticated message
sink.add([1, 2, 3]);
sink.add([4, 5]);
sink.add([6]);
// Calculate hash
sink.close();
final hash = await sink.hash();
print('SHA-512 hash: ${hash.bytes}');
}
Libraries
- cryptography_plus
- Cryptographic algorithms for Dart / Flutter developers.
- dart
- Cryptographic algorithms implemented in pure Dart.
- helpers
- Various helpers for cryptography.