cryptography 2.7.0 copy "cryptography: ^2.7.0" to clipboard
cryptography: ^2.7.0 copied to clipboard

Cryptographic algorithms for encryption, digital signatures, key agreement, authentication, and hashing. AES, Chacha20, ED25519, X25519, Argon2, and more. Good cross-platform support.

Pub Package Github Actions CI

Overview #

Popular cryptographic algorithms for Dart / Flutter developers.

Maintained by gohilla.com. 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!

Getting started #

If you use Flutter, it's recommended (but not necessarily) that you also import our sibling package cryptography_flutter, which delegates calls to Android / iOS / Mac OS X operating system APIs whenever possible.

In pubspec.yaml:

dependencies:
  cryptography: ^2.7.0
  cryptography_flutter: ^2.3.2 # Remove if you don't use Flutter

You are ready to go!

Issues and discussions #

Please report bugs at github.com/dint-dev/cryptography/issues.

Having questions? Feel free to use our Github Discussions at github.com/dint-dev/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 #

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.

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.
  • ChaCha20 / XChaCha20

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
    • Ecdh.p256 ( ECDH P256 / secp256r1 / prime256v1)
    • Ecdh.p384 ( ECDH P384 / secp384r1 / prime384v1)
    • Ecdh.p521 ( ECDH P521 / secp521r1 / prime521v1)
    • We don't have implementations of these in pure Dart.
  • 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:

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:

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, a popular signature algorithm:

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, a popular key agreement algorithm:

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 #

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/cryptography.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/cryptography.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/cryptography.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}');
}
268
likes
120
points
161k
downloads

Publisher

verified publisherdint.dev

Weekly Downloads

Cryptographic algorithms for encryption, digital signatures, key agreement, authentication, and hashing. AES, Chacha20, ED25519, X25519, Argon2, and more. Good cross-platform support.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

collection, crypto, ffi, js, meta, typed_data

More

Packages that depend on cryptography