encrypt

repository·5.x·Indexed 18 days ago

https://github.com/leocavalcante/encrypt

A set of high-level APIs over PointyCastle for two-way cryptography in Dart. It supports symmetric encryption (AES, Salsa20, Fernet) and asymmetric encryption (RSA), as well as RSA signing. The library includes a secure-random CLI for generating keys and IVs, an RSAKeyParser for PEM-formatted strings, and the Encrypter class for handling raw bytes and UTF-8 strings.

Tokens
7.5K
Snippets
27
Records
28
Agent score
62%

What's inside encrypt

  1. Generate secure random keys and IVs via CLI

    5.x

    The encrypt package provides a command-line tool to generate cryptographically secure random bytes, which can be used for keys and Initialization Vectors (IVs).

    1. Activate the package globally: pub global activate encrypt
    2. Run the secure-random command.

    You can customize the output using the --length and --base flags.

    $ secure-random --length 32 --base 64
  2. Explore encryption examples

    5.x

    The example/ directory contains implementation examples for various encryption and signing algorithms supported by the library. You can find specific implementations for:

    • AES: Symmetric encryption using AES.
    • AES-GCM: Symmetric encryption using AES in Galois/Counter Mode (authenticated encryption).
    • RSA: Asymmetric encryption using RSA.
    • Salsa20: Symmetric stream cipher.
    • Fernet: Symmetric encryption using the Fernet specification.
  3. Encrypt and decrypt using Fernet

    5.x

    Fernet provides authenticated symmetric encryption. It requires a 32-byte key. Note that the key used by the Fernet algorithm must be a base64-encoded 32-byte string.

    import 'package:encrypt/encrypt.dart';
    import 'dart:convert';
    
    void main() {
      final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
      final key = Key.fromUtf8('my32lengthsupersecretnooneknows1');
    
      // Fernet requires a base64Url encoded 32-byte key
      final b64key = Key.fromUtf8(base64Url.encode(key.bytes).substring(0,32));
      final fernet = Fernet(b64key);
      final encrypter = Encrypter(fernet);
    
      final encrypted = encrypter.encrypt(plainText);
      final decrypted = encrypter.decrypt(encrypted);
    
      print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
      print(fernet.extractTimestamp(encrypted.bytes)); // returns unix timestamp
    }
    import 'package:encrypt/encrypt.dart';
    import 'dart:convert';
    
    void main() {
      final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
      final key = Key.fromUtf8('my32lengthsupersecretnooneknows1');
    
      final b64key = Key.fromUtf8(base64Url.encode(key.bytes).substring(0,32));
      final fernet = Fernet(b64key);
      final encrypter = Encrypter(fernet);
    
      final encrypted = encrypter.encrypt(plainText);
      final decrypted = encrypter.decrypt(encrypted);
    
      print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
      print(fernet.extractTimestamp(encrypted.bytes)); // unix timestamp
    }
  4. Sign and verify data using RSA

    5.x

    Use the Signer class with RSASigner to create digital signatures. This example uses RSASignDigest.SHA256.

    import 'package:encrypt/encrypt.dart';
    import 'package:pointycastle/asymmetric/api.dart';
    
    // Assuming keys are loaded via parseKeyFromFile
    final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
    final privateKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
    
    final signer = Signer(RSASigner(RSASignDigest.SHA256, publicKey: publicKey, privateKey: privateKey));
    
    // Sign data
    final signature = signer.sign('hello world');
    print(signature.base64); 
    
    // Verify data
    final isValid = signer.verify64('hello world', 'SIGNATURE_BASE64_STRING');
    print(isValid);
     final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
     final privateKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
     final signer = Signer(RSASigner(RSASignDigest.SHA256, publicKey: publicKey, privateKey: privateKey));
    
     print(signer.sign('hello world').base64);
     print(signer.verify64('hello world', 'jfMhNM2v6hauQr6w3ji0xNOxGInHbeIH3DHlpf2W3vmSMyAuwGHG0KLcunggG4XtZrZPAib7oHaKEAdkHaSIGXAtEqaAvocq138oJ7BEznA4KVYuMcW9c8bRy5E4tUpikTpoO+okHdHr5YLc9y908CAQBVsfhbt0W9NClvDWegs='));
  5. Encrypt and decrypt using RSA (Asymmetric)

    5.x

    RSA is an asymmetric encryption algorithm. You can use parseKeyFromFile to load RSAPublicKey and RSAPrivateKey from PEM files. The Encrypter is initialized with both keys for two-way encryption.

    import 'dart:io';
    import 'package:encrypt/encrypt.dart';
    import 'package:pointycastle/asymmetric/api.dart';
    
    void main() async {
      final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
      final privKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
    
      final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
      final encrypter = Encrypter(RSA(publicKey: publicKey, privateKey: privKey));
    
      final encrypted = encrypter.encrypt(plainText);
      final decrypted = encrypter.decrypt(encrypted);
    
      print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
    }
  6. Encrypt and decrypt using Salsa20

    5.x

    Salsa20 is a symmetric stream cipher. It requires a Key and an IV (typically 8 bytes for Salsa20).

    import 'package:encrypt/encrypt.dart';
    
    void main() {
      final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
      final key = Key.fromLength(32);
      final iv = IV.fromLength(8);
      final encrypter = Encrypter(Salsa20(key));
    
      final encrypted = encrypter.encrypt(plainText, iv: iv);
      final decrypted = encrypter.decrypt(encrypted, iv: iv);
    
      print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
    }
  7. Encrypt and decrypt using AES

    5.x

    AES is a symmetric encryption algorithm. To use it, create a Key and an IV, then pass them to an Encrypter initialized with AES.

    Modes of Operation: The default mode is AESMode.sic. You can override this using the mode parameter in the AES constructor. Supported modes include:

    • AESMode.cbc (CBC)
    • AESMode.cfb64 (CFB-64)
    • AESMode.ctr (CTR)
    • AESMode.ecb (ECB)
    • AESMode.ofb64Gctr (OFB-64/GCTR)
    • AESMode.ofb64 (OFB-64)
    • AESMode.sic (SIC)

    Padding: By default, AES uses PKCS7 padding. To use no/zero padding, pass null to the padding parameter.

    import 'package:encrypt/encrypt.dart';
    
    void main() {
      final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
      final key = Key.fromUtf8('my 32 length key................');
      final iv = IV.fromLength(16);
    
      final encrypter = Encrypter(AES(key));
    
      final encrypted = encrypter.encrypt(plainText, iv: iv);
      final decrypted = encrypter.decrypt(encrypted, iv: iv);
    
      print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
    }
  8. Reference: secure-random CLI flags

    5.x

    The secure-random command-line tool supports the following flags:

    • -l, --length: The length of the bytes (defaults to "32")
    • -b, --base: Bytes represented as base 64 or base 16 (Hexdecimal) (defaults to "64")
    • -h, --[no-]help: Show this help message
    $ secure-random --help
  9. Use the Encrypter class to perform encryption and decryption

    5.x

    The Encrypter class acts as a high-level wrapper around an Algorithm. It provides convenient methods for handling both raw bytes (List<int> or Uint8List) and UTF-8 encoded strings.

    When initializing an Encrypter, you must provide an instance of an Algorithm (such as a symmetric or asymmetric algorithm implementation).

    Key Capabilities:

    • Encryption: Supports encrypting String inputs (automatically UTF-8 encoded) or List<int>/Uint8List inputs.
    • Decryption: Supports decrypting to List<int> or decoding directly back to a String.
    • Encoded String Decryption: Provides shorthand methods (decrypt16 and decrypt64) to decrypt data that has been pre-encoded as Hexadecimal or Base64 strings.
    // Example usage pattern
    final encrypter = Encrypter(myAlgorithm);
    
    // Encrypt a string
    final encrypted = encrypter.encrypt('my secret message');
    
    // Decrypt back to string
    final decrypted = encrypter.decrypt(encrypted);
  10. Use the Encrypted class to handle byte data

    5.x

    The Encrypted class is the base representation for any encrypted byte sequence. It provides multiple factory constructors to initialize data from different formats and getters to export the data back to common encodings.

    Constructors

    • Encrypted(Uint8List bytes): Initialize from raw bytes.
    • Encrypted.fromBase16(String encoded): Initialize from a hexadecimal string.
    • Encrypted.fromBase64(String encoded) or Encrypted.from64(String encoded): Initialize from a Base64 string.
    • Encrypted.fromUtf8(String input): Initialize from a UTF-8 string.
    • Encrypted.fromLength(int length): Generates a cryptographically secure random sequence of the specified length.
    • Encrypted.fromSecureRandom(int length): Alias for fromLength using SecureRandom.
    • Encrypted.allZerosOfLength(int length): Creates a sequence of zeros. Warning: This is not cryptographically secure.

    Getters

    • bytes: Returns the underlying Uint8List.
    • base16: Returns the hexadecimal string representation.
    • base64: Returns the Base64 string representation.
    // Create from random bytes
    final encrypted = Encrypted.fromLength(32);
    
    // Export to hex
    String hex = encrypted.base16;
    
    // Create from existing hex
    final existing = Encrypted.fromBase16(hex);
  11. Use the AES class for symmetric encryption

    5.x

    The AES class implements the Algorithm interface for symmetric encryption using the Advanced Encryption Standard. It requires a Key and supports various modes and padding schemes.

    Constructor

    AES(this.key, {this.mode = AESMode.sic, this.padding = 'PKCS7'})

    • key: An instance of Key containing the secret bytes.
    • mode: An AESMode value (defaults to AESMode.sic).
    • padding: A string specifying the padding scheme (defaults to 'PKCS7'). If set to null, no padding is applied.

    Methods

    encrypt

    Encrypted encrypt(Uint8List bytes, {IV? iv, Uint8List? associatedData}) Encrypts the provided Uint8List bytes.

    • Note: If the mode is not AESMode.ecb, an IV (Initialization Vector) must be provided, otherwise a StateError('IV is required.') is thrown.
    • associatedData: Optional data used for authenticated encryption modes like AESMode.gcm.

    decrypt

    Uint8List decrypt(Encrypted encrypted, {IV? iv, Uint8List? associatedData}) Decrypts the provided Encrypted object.

    • Note: If the mode is not AESMode.ecb, an IV must be provided, otherwise a StateError('IV is required.') is thrown.
    • associatedData: Optional data used for authenticated encryption modes like AESMode.gcm.
    // Example usage of AES encryption
    final key = Key.fromBytes(myBytes);
    final aes = AES(key, mode: AESMode.cbc, padding: 'PKCS7');
    final iv = IV.fromBytes(myIvBytes);
    
    final encrypted = aes.encrypt(dataBytes, iv: iv);
    final decrypted = aes.decrypt(encrypted, iv: iv);
  12. Use the Fernet algorithm for symmetric encryption

    5.x

    The Fernet class implements the Fernet symmetric encryption algorithm. It requires a 32-byte key, which is internally split into a 16-byte signing key and a 16-byte encryption key.

    Key Requirements

    • The key must be exactly 32 bytes.
    • The key must be url-safe base64-encoded bytes.
    • If the key length is not 32, a StateError is thrown.

    Encryption

    Use the encrypt method to transform Uint8List bytes into an Encrypted object. If no IV (Initialization Vector) is provided, one is automatically generated using secure random bytes.

    Decryption

    Use the decrypt method to recover the original Uint8List.

    • TTL (Time To Live): You can provide an optional ttl (integer) to ensure the token is only valid for a certain duration from its creation timestamp.
    • IV Handling: You should not provide a custom IV during decryption; the Fernet token format embeds the IV within the data, and the method will infer it automatically. Providing an IV will result in a StateError.
    • Validation: The method performs signature verification and checks for clock skew and TTL expiration. If any check fails, it throws a StateError('Invalid token').
    // Example usage of Fernet
    final key = Key.fromBytes(Uint8List(32)); // Must be 32 bytes
    final fernet = Fernet(key);
    
    // Encrypting data
    final originalData = Uint8List.fromList("hello world".codeUnits);
    final encrypted = fernet.encrypt(originalData);
    
    // Decrypting data with a TTL of 60 seconds
    try {
      final decrypted = fernet.decrypt(encrypted, ttl: 60);
      print(String.fromCharCodes(decrypted));
    } catch (e) {
      print("Decryption failed: $e");
    }