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.6.1
  cryptography_flutter: ^2.3.1 # 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 algorithms

The following 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!
}

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

Libraries

cryptography
Cryptographic algorithms for Dart / Flutter developers.
dart
Cryptographic algorithms implemented in pure Dart.
helpers
Various helpers for cryptography.