infusion_ffi 1.5.1 copy "infusion_ffi: ^1.5.1" to clipboard
infusion_ffi: ^1.5.1 copied to clipboard

Flutter plugin for the Infusion crypto vault — AEAD encryption, Ed25519 signing, BLAKE3 CIDs, BIP-39 wallets, and capability tokens. Prebuilt binaries for all platforms.

example/lib/main.dart

import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:infusion_ffi/infusion_ffi.dart';

void main() {
  runApp(const InfusionExampleApp());
}

class InfusionExampleApp extends StatelessWidget {
  const InfusionExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'infusion_ffi example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
        useMaterial3: true,
      ),
      home: const _ExamplePage(),
    );
  }
}

class _ExamplePage extends StatefulWidget {
  const _ExamplePage();

  @override
  State<_ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<_ExamplePage> {
  final List<_Result> _results = [];
  bool _running = false;

  Future<void> _run() async {
    setState(() {
      _results.clear();
      _running = true;
    });

    try {
      await _runMnemonic();
      await _runSealOpen();
      await _runSignVerify();
      await _runCapability();
      await _runKeyDerivation();
      await _runHashing();
    } catch (e) {
      _add('Error', e.toString(), ok: false);
    } finally {
      setState(() => _running = false);
    }
  }

  // ── BIP-39 mnemonic ──────────────────────────────────────────────────────

  Future<void> _runMnemonic() async {
    final phrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
    _add('mnemonicGenerate (12)', phrase);

    final json = await InfusionFFI.mnemonicRestore(phrase);
    final keys = jsonDecode(json) as Map<String, dynamic>;
    _add(
      'mnemonicRestore',
      'enc_key_hex: ${_short(keys['enc_key_hex'] as String)}\n'
          'sign_seed_hex: ${_short(keys['sign_seed_hex'] as String)}',
    );
  }

  // ── Encrypt / decrypt ────────────────────────────────────────────────────

  Future<void> _runSealOpen() async {
    final phrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
    final keys = jsonDecode(await InfusionFFI.mnemonicRestore(phrase))
        as Map<String, dynamic>;

    final vault = await InfusionFFI.create(
      encKeyHex: keys['enc_key_hex'] as String,
      signSeedHex: keys['sign_seed_hex'] as String,
    );

    try {
      const message = 'Hello, Infusion!';
      final frame = await vault.seal(
        data: Uint8List.fromList(utf8.encode(message)),
        policyId: 0,
      );
      _add('seal', '${frame.length} bytes frame');

      final plain = await vault.open(frame);
      final recovered = utf8.decode(plain);
      _add('open', recovered, ok: recovered == message);

      final ok = await vault.verify(frame);
      _add('verify', ok.toString(), ok: ok);

      final detailed = await vault.verifyDetailed(frame);
      _add(
        'verifyDetailed',
        'ok=${detailed.ok}  code=${detailed.code}  '
            'cid=${_short(_hex(detailed.cid))}',
        ok: detailed.ok,
      );
    } finally {
      await vault.dispose();
      _add('dispose', 'vault released');
    }
  }

  // ── Ed25519 signatures ───────────────────────────────────────────────────

  Future<void> _runSignVerify() async {
    final phrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
    final keys = jsonDecode(await InfusionFFI.mnemonicRestore(phrase))
        as Map<String, dynamic>;
    final seed32 = _fromHex(keys['sign_seed_hex'] as String);

    final message = Uint8List.fromList(utf8.encode('signed content'));
    final sig64 = await InfusionFFI.signWithSeed(seed32, message);
    _add('signWithSeed', '${sig64.length} bytes signature');

    final pub32 = await InfusionFFI.derivePubkey(seed32);
    _add('derivePubkey', _short(_hex(pub32)));

    final valid = await InfusionFFI.verifySignature(
      pub32: pub32,
      msg: message,
      sig64: sig64,
    );
    _add('verifySignature', valid.toString(), ok: valid);

    // Tamper detection
    final tampered = Uint8List.fromList(message)..last ^= 0xFF;
    final invalid = await InfusionFFI.verifySignature(
      pub32: pub32,
      msg: tampered,
      sig64: sig64,
    );
    _add('verifySignature (tampered)', 'correctly rejected: ${!invalid}',
        ok: !invalid);
  }

  // ── Capability tokens ────────────────────────────────────────────────────

  Future<void> _runCapability() async {
    final phrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
    final keys = jsonDecode(await InfusionFFI.mnemonicRestore(phrase))
        as Map<String, dynamic>;

    final vault = await InfusionFFI.create(
      encKeyHex: keys['enc_key_hex'] as String,
      signSeedHex: keys['sign_seed_hex'] as String,
    );

    try {
      final recipientPhrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
      final recipientKeys =
          jsonDecode(await InfusionFFI.mnemonicRestore(recipientPhrase))
              as Map<String, dynamic>;
      final recipientPub32 = await InfusionFFI.derivePubkey(
        _fromHex(recipientKeys['sign_seed_hex'] as String),
      );

      final resourceCid = await InfusionFFI.cid(
        Uint8List.fromList(utf8.encode('resource data')),
      );

      final token = await vault.issueCap(
        scopeCid: resourceCid,
        rights: 0x01, // READ
        expTs: DateTime.now()
                .add(const Duration(hours: 1))
                .millisecondsSinceEpoch ~/
            1000,
        delegatedPub32: recipientPub32,
      );
      _add('issueCap', '${token.length} bytes token');

      final valid = await vault.verifyCap(
        capToken: token,
        requesterPub32: recipientPub32,
      );
      _add('verifyCap', valid.toString(), ok: valid);
    } finally {
      await vault.dispose();
    }
  }

  // ── Key derivation ───────────────────────────────────────────────────────

  Future<void> _runKeyDerivation() async {
    final phrase = await InfusionFFI.mnemonicGenerate(wordCount: 12);
    final keys = jsonDecode(await InfusionFFI.mnemonicRestore(phrase))
        as Map<String, dynamic>;

    final vault = await InfusionFFI.create(
      encKeyHex: keys['enc_key_hex'] as String,
      signSeedHex: keys['sign_seed_hex'] as String,
    );

    try {
      final context = Uint8List.fromList(utf8.encode('storage:user_box'));
      final derived = await vault.deriveKey(context);
      _add('deriveKey', '${derived.length} bytes: ${_short(_hex(derived))}');

      final idx = await vault.blindIndex('user@example.com');
      _add('blindIndex', '${idx.length} bytes: ${_short(_hex(idx))}');
    } finally {
      await vault.dispose();
    }
  }

  // ── Hashing ──────────────────────────────────────────────────────────────

  Future<void> _runHashing() async {
    final data = Uint8List.fromList(utf8.encode('hello world'));

    final cid = await InfusionFFI.cid(data);
    _add('cid (BLAKE3)', _short(_hex(cid)));

    final sha = await InfusionFFI.sha256(data);
    _add('sha256', _short(_hex(sha)));

    final shaHex = await InfusionFFI.sha256Hex('nonce_value');
    _add('sha256Hex', _short(shaHex));
  }

  // ── Helpers ───────────────────────────────────────────────────────────────

  void _add(String label, String value, {bool ok = true}) {
    setState(() => _results.add(_Result(label: label, value: value, ok: ok)));
  }

  static String _short(String s) =>
      s.length > 28 ? '${s.substring(0, 14)}…${s.substring(s.length - 6)}' : s;

  static String _hex(Uint8List b) =>
      b.map((e) => e.toRadixString(16).padLeft(2, '0')).join();

  static Uint8List _fromHex(String hex) {
    final result = Uint8List(hex.length ~/ 2);
    for (var i = 0; i < result.length; i++) {
      result[i] = int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16);
    }
    return result;
  }

  // ── UI ────────────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('infusion_ffi example'),
        backgroundColor: colorScheme.inversePrimary,
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: FilledButton.icon(
              onPressed: _running ? null : _run,
              icon: _running
                  ? const SizedBox(
                      width: 18,
                      height: 18,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        color: Colors.white,
                      ),
                    )
                  : const Icon(Icons.play_arrow_rounded),
              label: Text(_running ? 'Running…' : 'Run all demos'),
            ),
          ),
          Expanded(
            child: ListView.separated(
              padding: const EdgeInsets.fromLTRB(16, 0, 16, 32),
              itemCount: _results.length,
              separatorBuilder: (_, __) => const Divider(height: 1),
              itemBuilder: (_, i) {
                final r = _results[i];
                return ListTile(
                  dense: true,
                  leading: Icon(
                    r.ok ? Icons.check_circle : Icons.cancel,
                    color: r.ok ? Colors.green : Colors.red,
                    size: 20,
                  ),
                  title: Text(
                    r.label,
                    style: const TextStyle(
                      fontWeight: FontWeight.w600,
                      fontSize: 13,
                    ),
                  ),
                  subtitle: Text(
                    r.value,
                    style: TextStyle(
                      fontSize: 12,
                      color: colorScheme.onSurfaceVariant,
                      fontFamily: 'monospace',
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

class _Result {
  const _Result({
    required this.label,
    required this.value,
    required this.ok,
  });

  final String label;
  final String value;
  final bool ok;
}
0
likes
130
points
559
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter plugin for the Infusion crypto vault — AEAD encryption, Ed25519 signing, BLAKE3 CIDs, BIP-39 wallets, and capability tokens. Prebuilt binaries for all platforms.

Topics

#cryptography #encryption #security #ffi

License

MIT (license)

Dependencies

crypto, ffi, flutter, flutter_web_plugins, path, web

More

Packages that depend on infusion_ffi

Packages that implement infusion_ffi