Skip to main content

air_link_secure 0.2.1 copy "air_link_secure: ^0.2.1" to clipboard
air_link_secure: ^0.2.1 copied to clipboard

End-to-end encryption (E2EE) with Diffie-Hellman key exchange for Air Link.

Air Link Secure #

End-to-end encryption for Air Link: ECDSA identity keys, mutual auth handshake, and AES-256-GCM session encryption.

final identity = await Identity.loadOrCreate();
final session = await connectSecure('192.168.1.10', identity,
    heartbeat: HeartbeatConfig(), autoReconnect: true);
session.peer.onMessage.listen((msg) {
  print('🔐 $msg');  // automatically decrypted
});

Features:

Area Algorithm / Protocol
Identity keys ECDSA secp256r1 (persisted to disk)
Key exchange Diffie-Hellman (RFC 3526 Group 14)
Key derivation HKDF-SHA256
Auth protocol SIGMA-like (3 messages), mutual authentication
Trust model Trust On First Use (TOFU)
Session encryption AES-256-GCM, random 12-byte IV per packet

Quick Start #

1. Add the dependency #

dependencies:
  air_link_secure: ^0.2.0

2. Generate an identity #

Every device needs a persistent ECDSA identity key pair. It is automatically generated on first run and stored in a JSON file:

import 'package:air_link_secure/air_link_secure.dart';

final identity = await Identity.loadOrCreate(storagePath: 'identity.json');
print('My fingerprint: ${identity.fingerprint}');
// → "a1b2c3d4e5f6..."

3. Connect with automatic authentication #

// On device A — host
final identity = await Identity.loadOrCreate();
final sessions = await listenSecure(identity, port: 8080);
sessions.listen((session) {
  print('Securely connected: ${session.peer.id}');
});

// On device B — connect
final identity = await Identity.loadOrCreate();
final session = await connectSecure('192.168.1.10', identity, port: 8080);
await session.peer.sendText('This message is encrypted');

The handshake happens automatically:

  1. Client sends its DH public key + identity public key
  2. Server responds with its DH key, identity key, and a signature
  3. Client verifies the signature, stores the server's key (TOFU)
  4. Client sends its own signature
  5. Server verifies, stores client's key (TOFU)
  6. Both sides derive the shared AES-256-GCM key and wrap the transport

API #

Identity #

Method / Property Description
Identity.loadOrCreate({String? storagePath}) Load from disk or generate a fresh key pair
Identity.generate({String? storagePath}) Generate a brand-new key pair
Uint8List get publicKeyBytes Raw public key (65 bytes, uncompressed)
String get fingerprint SHA-256 fingerprint of the public key (hex)
static String fingerprintOf(Uint8List pubKeyBytes) Compute fingerprint of any public key
Uint8List sign(Uint8List data) Sign data with the private key
static bool verify(Uint8List data, Uint8List sig, Uint8List pubKey) Verify an ECDSA signature
Uint8List? trustedPeerKey(String fingerprint) Look up a trusted peer's public key
void trustPeer(String fingerprint, Uint8List pubKeyBytes) Manually trust a peer
Future<void> save() Persist identity (including TOFU database) to disk

KeyExchange #

Method Description
KeyExchange() Generate ephemeral DH key pair
Uint8List get publicKeyBytes DH public key (256 bytes, RFC 3526 Group 14)
BigInt computeSharedSecret(BigInt peerPublic) Compute shared secret
Uint8List deriveKey(BigInt sharedSecret) Derive 32-byte AES-256 key via HKDF-SHA256

SecureTransport #

Feature Description
SecureTransport(Transport inner, Uint8List key) Wrap any transport with AES-256-GCM
send(Uint8List data) Encrypt and send
onData Receive decrypted data
Wire format [4B encLen][12B IV][ciphertext + 16B GCM tag]

Convenience functions #

Function Description
connectSecure(host, identity, {port, heartbeat, autoReconnect}) Connect with full auth + encryption
listenSecure(identity, {port, heartbeat}) Listen for secure connections

Auto-Reconnect #

Pass autoReconnect: true to automatically re-establish a broken secure connection. The handshake runs again on reconnect:

final session = await connectSecure('192.168.1.10', identity,
    autoReconnect: true,
    heartbeat: HeartbeatConfig(interval: Duration(seconds: 5)));

Platform Support #

Platform Status
Android ✅
iOS ✅
macOS ✅
Windows ✅
Linux ✅

Zero Dependencies #

All cryptography is built with pointycastle: ^4.0.0 (pure Dart, no native bindings). No OpenSSL, no BoringSSL, no platform channels.


Module Integration #

// Secure + Discovery: find peers, then connect securely
final fp = identity.fingerprint;
await DiscoveryService.advertise(port: 8080, identityFingerprint: fp);
DiscoveryService.discover().listen((peer) {
  if (peer.identityFingerprint != null) {
    connectSecure(peer.ip, identity, port: peer.port);
  }
});
// Secure + File: encrypted file transfer
final session = await connectSecure('192.168.1.10', identity);
final sender = FileSender(session.peer);
await sender.share(confidentialFile);  // encrypted automatically

Requirements #

  • Dart SDK >=3.0.0 <4.0.0
  • Depends on air_link_core and pointycastle: ^4.0.0

License #

MIT

0
likes
140
points
51
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

End-to-end encryption (E2EE) with Diffie-Hellman key exchange for Air Link.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

air_link_core, pointycastle

More

Packages that depend on air_link_secure