confidential 0.5.0 copy "confidential: ^0.5.0" to clipboard
confidential: ^0.5.0 copied to clipboard

Dart literals obfuscator to defend against static reverse engineering.

Dart Confidential #

Pub Version Dart License

A highly configurable and performant tool for obfuscating Dart literals embedded in the application code that you should protect from static code analysis, making the app more resistant to reverse engineering.

This is a Dart port of the Swift Confidential project by SecureVale, bringing the same powerful obfuscation capabilities to the Dart ecosystem.

Simply integrate the tool with your Dart project, configure your own obfuscation algorithm along with the list of secret literals, and build the project 🚀

Motivation #

Pretty much every single app has at least few literals embedded in code, those include: URLs, various client identifiers (e.g. API keys), pinning data (e.g. X.509 certificates or SPKI digests), database connection strings, RASP-related literals (e.g. list of suspicious packages or file paths for tamper detection), and many other context-specific literals. While the listed examples of code literals might seem innocent, not obfuscating them, in many cases, can be considered as giving a handshake to the potential threat actor. This is especially true in security-sensitive apps, such as mobile banking apps, 2FA authenticator apps and password managers.

This tool aims to provide an elegant and maintainable solution to the above problem by introducing the composable obfuscation techniques that can be freely combined to form an algorithm for obfuscating selected Dart literals.

Note: While Dart Confidential certainly makes the static analysis of the code more challenging, it is by no means the only code hardening technique that you should employ to protect your app against reverse engineering and tampering. To achieve a decent level of security, we highly encourage you to supplement this tool's security measures with runtime application self-protection (RASP) checks, as well as Dart code obfuscation. With that said, no security measure can ever guarantee absolute security. Any motivated and skilled enough attacker will eventually bypass all security protections. For this reason, always keep your threat models up to date.

Getting Started #

Begin by creating a confidential.yaml YAML configuration file in the root directory of your Dart project. At minimum, the configuration must contain obfuscation algorithm and one or more secret definitions.

For example, a configuration file for a hypothetical security module could look like this:

algorithm:
  - encrypt using aes-192-gcm
  - shuffle

defaultNamespace: create Secrets

secrets:
  - name: suspiciousDynamicLibraries
    value:
      - Substrate
      - Substitute
      - FridaGadget
      # ... other suspicious libraries
  - name: suspiciousFilePaths
    value:
      - /.installed_unc0ver
      - /usr/sbin/frida-server
      - /private/var/lib/cydia
      # ... other suspicious file paths

Warning: The algorithm from the above configuration serves as example only, do not use this particular algorithm in your production code. Instead, compose your own algorithm from the obfuscation techniques described below and don't share your algorithm with anyone.

Having created the configuration file, you can use the dart-confidential CLI tool to generate Dart code with obfuscated secret literals:

dart run dart-confidential obfuscate --configuration confidential.yaml --output lib/generated/confidential.dart

Upon successful command execution, the generated confidential.dart file will contain code similar to the following:

// GENERATED CODE - DO NOT MODIFY BY HAND
// Generated by dart-confidential

import 'package:confidential/confidential.dart';
import 'dart:typed_data';
import 'dart:convert';

class ObfuscatedLiterals {

  static final suspiciousDynamicLibraries = ObfuscatedValue<List<String>>(
    Secret(
      data: Uint8List.fromList([0x14, 0x4b, 0xe5, 0x48, /* ... */]),
      nonce: 13452749969377545032,
    ),
    _deobfuscateData,
  );

  static final suspiciousFilePaths = ObfuscatedValue<List<String>>(
    Secret(
      data: Uint8List.fromList([0x04, 0xdf, 0x99, 0x61, /* ... */]),
      nonce: 4402772458530791297,
    ),
    _deobfuscateData,
  );

  static T _deobfuscateData<T>(Uint8List data, int nonce) {
    // Deobfuscation implementation
  }
}

You can then, for example, iterate over a deobfuscated array of suspicious dynamic libraries in your own code using the projected value of the generated suspiciousDynamicLibraries property:

final suspiciousLibraries = ObfuscatedLiterals.suspiciousDynamicLibraries.$
    .map((lib) => lib.toLowerCase())
    .toList();

final checkPassed = loadedLibraries
    .every((lib) => !suspiciousLibraries.any((suspicious) =>
        lib.toLowerCase().contains(suspicious)));

Installation #

Add this package to your pubspec.yaml:

dependencies:
  confidential: ^0.4.0

dev_dependencies:
  confidential: ^0.4.0

Then run:

dart pub get

Configuration #

Dart Confidential supports a number of configuration options, all of which are stored in a single YAML configuration file.

YAML configuration keys #

Key Value type Description
algorithm List of strings The list of obfuscation techniques representing individual steps that are composed together to form the obfuscation algorithm. See Obfuscation techniques section for usage details. Required.
defaultAccessModifier String The default access-level modifier applied to each generated secret literal, unless the secret definition states otherwise. The default value is internal.
defaultNamespace String The default namespace in which to enclose all the generated secret literals without explicitly assigned namespace. The default value is create Secrets.
experimentalMode Boolean Specifies whether to use experimental mode. The default value is false.
internalImport Boolean Specifies whether to generate internal import statements. The default value is false.
secrets List of objects The list of objects defining the secret literals to be obfuscated. See Secrets section for usage details. Required.

Obfuscation techniques #

The obfuscation techniques are the composable building blocks from which you can create your own obfuscation algorithm. You can compose them in any order you want, so that no one except you knows how the secret literals are obfuscated.

Encryption

This technique involves data encryption using the algorithm of your choice. The encryption technique is polymorphic, meaning that given the same input data, different output data is produced with each run.

Syntax

encrypt using <algorithm>

Symmetric Encryption Algorithms:

  • aes-128-gcm - The Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM) with 128-bit key
  • aes-192-gcm - The Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM) with 192-bit key
  • aes-256-gcm - The Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM) with 256-bit key 🔒 Recommended
  • chacha20-poly1305 - The ChaCha20-Poly1305 authenticated encryption algorithm

Asymmetric Encryption Algorithms:

  • rsa-2048 - RSA encryption with 2048-bit keys and OAEP padding
  • rsa-4096 - RSA encryption with 4096-bit keys and OAEP padding 🔒 High Security
  • rsa-2048-sha256 - RSA-2048 with SHA-256 hash function
  • rsa-4096-sha256 - RSA-4096 with SHA-256 hash function
  • rsa-2048-sha512 - RSA-2048 with SHA-512 hash function
  • rsa-4096-sha512 - RSA-4096 with SHA-512 hash function 🔒 Maximum Security

Compression

This technique involves data compression using the algorithm of your choice. In general, the compression technique is non-polymorphic, meaning that given the same input data, the same output data is produced with each run. However, Dart Confidential applies additional polymorphic obfuscation routines to mask the bytes identifying the compression algorithm used.

Syntax

compress using <algorithm>

Supported algorithms:

  • zlib - The zlib compression algorithm
  • gzip - The GZip compression algorithm
  • bzip2 - The BZip2 compression algorithm
  • lz4 - The LZ4 compression algorithm
  • lzfse - The LZFSE compression algorithm (fallback to gzip)
  • lzma - The LZMA compression algorithm (fallback to bzip2)

Randomization

This technique involves data randomization. The randomization technique is polymorphic, meaning that given the same input data, different output data is produced with each run.

Syntax

shuffle

Secrets #

The configuration file utilizes YAML objects to describe the secret literals, which are to be obfuscated.

Key Value type Description
name String The name of the generated Dart property containing obfuscated secret literal's data. This value is used as-is, without validity checking. Thus, make sure to use a valid property name. Required.
value String or List of strings The plain value of the secret literal, which is to be obfuscated. The YAML data types are mapped to String and List<String> in Dart, respectively. Required.
accessModifier String The access-level modifier of the generated Dart property containing obfuscated secret literal's data. The supported values are internal, public and private. If not specified, the top-level defaultAccessModifier value is used.
namespace String The namespace in which to enclose the generated secret literal declaration.

Namespaces #

In accordance with Dart programming best practices, Dart Confidential encapsulates generated secret literal declarations in namespaces (i.e. classes). The namespaces syntax allows you to either create a new namespace or extend an existing one.

Syntax

create <namespace>                    # creates new namespace
extend <namespace> [from <module>]    # extends existing namespace, optionally specifying
                                      # the module to which this namespace belongs

Example usage

Assuming that you would like to keep the generated secret literal declaration(s) in a new namespace named Secrets, use the following YAML code:

defaultNamespace: create Secrets

If, however, you would rather like to keep the generated secret literal declaration(s) in an existing namespace named Pinning and imported from Crypto module, use the following YAML code instead:

namespace: extend Pinning from Crypto

🔒 Enhanced Security Features #

Key Management and Rotation #

Dart Confidential now supports advanced key management with automatic key rotation for enhanced security:

keyManagement:
  enableRotation: true              # Enable automatic key rotation
  rotationIntervalDays: 30          # Rotate keys every 30 days
  maxOldKeys: 3                     # Keep 3 old keys for backward compatibility
  keyDerivationFunction: PBKDF2     # Use PBKDF2 for key derivation (or SCRYPT)
  keyDerivationIterations: 100000   # Number of iterations for key strengthening
  salt: "your-unique-salt-here"     # Custom salt for key derivation

Key Derivation Functions:

  • PBKDF2 - Password-Based Key Derivation Function 2 (recommended for most use cases)
  • SCRYPT - Memory-hard key derivation function (higher security, more resource intensive)

Benefits:

  • 🔄 Automatic Key Rotation: Keys are automatically rotated based on your schedule
  • 🔙 Backward Compatibility: Old keys are retained for decrypting existing data
  • 🛡️ Strong Key Derivation: PBKDF2/SCRYPT with configurable iterations
  • 🔐 Version Management: Each key has a version for proper tracking

Algorithm Selection Guide #

For Maximum Security (Recommended):

algorithm:
  - encrypt using aes-256-gcm
  - compress using bzip2
  - encrypt using chacha20-poly1305
  - shuffle

For High-Security Asymmetric Encryption:

algorithm:
  - encrypt using rsa-4096-sha512
  - compress using lz4

For Balanced Security and Performance:

algorithm:
  - encrypt using aes-256-gcm
  - shuffle

Usage #

The easiest way to use dart-confidential is with build_runner, which automatically generates obfuscated code when you build your project:

  1. Add build_runner to your pubspec.yaml:
dev_dependencies:
  build_runner: ^2.4.7
  confidential: ^0.4.0
  1. Create a confidential.yaml configuration file in your project root
  2. Run the build:
dart run build_runner build

The obfuscated code will be automatically generated in lib/generated/confidential.dart.

CLI Usage #

You can also use the command-line tool directly:

# Basic usage
dart run dart-confidential obfuscate -c confidential.yaml -o lib/generated/confidential.dart

# Show help
dart run dart-confidential --help

# Show version
dart run dart-confidential --version

Commands #

  • obfuscate - Obfuscate literals based on configuration

Options #

  • -c, --configuration - Path to the configuration file (required)
  • -o, --output - Output file path (required)
  • -h, --help - Show help message
  • -v, --version - Show version information

Examples #

See the example/ directory for complete examples:

  • example/confidential.yaml - Example configuration file
  • example/confidential_example.dart - Example usage
  • example/ - Complete Flutter app demonstrating build_runner integration

Running the Flutter Example #

  1. Navigate to the example directory:
cd example
  1. Get dependencies:
dart pub get
  1. Generate obfuscated code:
dart run build_runner build
  1. Run the Flutter app:
flutter run

The example app demonstrates how to use obfuscated literals in a real Flutter application.

Security Considerations #

Warning: The example algorithms in this documentation are for demonstration purposes only. Do not use these particular algorithms in your production code. Instead, compose your own algorithm from the available obfuscation techniques and don't share your algorithm with anyone.

Following secure SDLC best practices, consider not committing the production algorithm in your repository, but instead configure your CI/CD pipeline to run a custom script (ideally just before the build step), which will modify the configuration file by replacing the algorithm value with the one retrieved from the secrets vault.

Differences from Swift Confidential #

This Dart implementation maintains feature parity with the original Swift Confidential while adapting to Dart's ecosystem:

  • Property Wrappers → ObfuscatedValue: Dart doesn't have property wrappers, so we use ObfuscatedValue<T> classes instead
  • Macros → Code Generation: Instead of Swift macros, we use a CLI tool for code generation
  • Package Manager: Uses pub instead of Swift Package Manager
  • Platform Support: Supports all Dart platforms (Flutter, web, server, etc.)

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Acknowledgments #

This project is a Dart port of Swift Confidential by SecureVale. We thank the original authors for their excellent work and for making it available under an open-source license.

7
likes
130
points
69
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dart literals obfuscator to defend against static reverse engineering.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

archive, args, build, convert, crypto, pointycastle, yaml

More

Packages that depend on confidential