cryptography 2.3.0 cryptography: ^2.3.0 copied to clipboard
Cryptographic algorithms for encryption, digital signatures, key agreement, authentication, and hashing. AES, Chacha20, ED25519, X25519, and more. Web Crypto support.
Overview #
Popular cryptographic algorithms for Dart / Flutter developers.
Maintained by Gohilla. Licensed under the Apache License 2.0.
This package is:
- 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 #
In pubspec.yaml:
dependencies:
cryptography: ^2.3.0
cryptography_flutter: ^2.2.0 # Remove this if you don't use Flutter
You are ready to go!
Concepts #
Cryptographic keys #
The usual arguments to algorithms are:
- SecretKey
- Used by ciphers, message authentication codes, and key derivation functions.
- KeyPair
- SimpleKeyPair (Byte sequences such as Ed25519 / X25519 32-byte private keys)
- EcKeyPairData (P-256, P-384, P-521 private keys)
- RsaKeyPairData (RSA private keys)
- PublicKey
- SimplePublicKey (Byte 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 a bit higher API abstraction, we encourage developers to use Wand instances for performing operations without visible keys (thus "magic wands"). There are currently three types of wands:
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.
Algorithms by type #
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 algorithms #
The following 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)
Random number generators #
We continue to use the old good Random.secure()
as the default random number in all APIs. It
generates only up to 1 MB of random data per second, but this is rarely a performance bottleneck.
If you do want 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's hard to imagine a scenario where an attacker could use the generated random numbers to break the security of the system.
If you need a deterministic random number generator for tests, the package contains ChachaRandom.forTesting.
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.
- SHA1 / SHA2 uses implementation in package:crypto, which is maintained by Google. The rest of the algorithms in DartCryptography are written and tested by us.
- 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
- 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/cryptography.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.
import 'package:cryptography/cryptography.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.
import 'package:cryptography/cryptography.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 aliceKeyPair.extractBytes();
print('Shared secret: $sharedSecretBytes');
}
Authenticated encryption #
In this example, we encrypt a message with AesCtr and append a Hmac message authentication code.
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
Future<void> main() async {
// Message we want to encrypt
final message = utf8.encode('Hello encryption!');
// Choose the cipher
final algorithm = AesCtr(macAlgorithm: Hmac.sha256());
// Generate a random secret key.
final secretKey = await algorithm.newSecretKey();
final secretKeyBytes = await secretKey.extractBytes();
print('Secret key: ${secretKeyBytes}');
// Encrypt
final secretBox = await algorithm.encrypt(
message,
secretKey: secretKey,
);
print('Nonce: ${secretBox.nonce}');
print('Ciphertext: ${secretBox.cipherText}');
print('MAC: ${secretBox.mac.bytes}');
// Decrypt
final clearText = await algorithm.decrypt(
secretBox,
secretKey: secretKey,
);
print('Cleartext: ${utf8.decode(clearText)}');
}
Hashing #
In this example, we use Sha512.
import 'package:cryptography/cryptography.dart';
Future<void> main() async {
final sink = Sha512().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}');
}