abdm_fidelius 0.1.0
abdm_fidelius: ^0.1.0 copied to clipboard
Dart implementation of ABDM Fidelius encryption (ECDH + HKDF + AES-256-GCM) for India's health data exchange.
// ignore_for_file: avoid_print
/// Complete example of the abdm_fidelius package.
///
/// Covers:
/// 1. Basic usage — generate keys, encrypt, decrypt
/// 2. Per-curve selection — X25519 and P-256
/// 3. Healthcare context — encrypting a FHIR bundle for ABDM
/// 4. Low-level primitives — step-by-step protocol execution
/// 5. JSON serialization — payload and key pair round-trip
library;
import 'dart:convert';
import 'package:abdm_fidelius/abdm_fidelius.dart';
Future<void> main() async {
await basicUsage();
await perCurveSelection();
await healthcareExample();
await lowLevelPrimitives();
await jsonSerialization();
}
// ---------------------------------------------------------------------------
// 1. Basic usage — Weierstrass Curve25519 (ABDM default)
// ---------------------------------------------------------------------------
Future<void> basicUsage() async {
print('=== 1. Basic Usage (Weierstrass Curve25519) ===\n');
const fidelius = FideliusCurve25519();
// Both parties generate key pairs
final sender = await fidelius.generateKeyPair();
final receiver = await fidelius.generateKeyPair();
print('Sender public key: ${sender.publicKey.substring(0, 20)}...');
print('Receiver public key: ${receiver.publicKey.substring(0, 20)}...');
// Sender encrypts with receiver's public key and nonce
final encrypted = await fidelius.encrypt(
plaintext: 'Hello from abdm_fidelius!',
senderKeyPair: sender,
receiverPublicKey: receiver.publicKey,
receiverNonce: receiver.nonce,
);
print('Encrypted data: ${encrypted.encryptedData.substring(0, 20)}...');
// Receiver decrypts with their own key pair
final decrypted = await fidelius.decrypt(
payload: encrypted,
receiverKeyPair: receiver,
);
print('Decrypted: $decrypted');
print('');
}
// ---------------------------------------------------------------------------
// 2. Per-curve selection — X25519 and P-256
// ---------------------------------------------------------------------------
Future<void> perCurveSelection() async {
print('=== 2. Per-Curve Selection ===\n');
// X25519 (RFC 7748 Montgomery form)
const x25519 = FideliusX25519();
final xSender = await x25519.generateKeyPair();
final xReceiver = await x25519.generateKeyPair();
final xEncrypted = await x25519.encrypt(
plaintext: 'Encrypted with X25519',
senderKeyPair: xSender,
receiverPublicKey: xReceiver.publicKey,
receiverNonce: xReceiver.nonce,
);
final xDecrypted = await x25519.decrypt(
payload: xEncrypted,
receiverKeyPair: xReceiver,
);
print('X25519 result: $xDecrypted');
// P-256 (NIST secp256r1)
const p256 = FideliusP256();
final pSender = await p256.generateKeyPair();
final pReceiver = await p256.generateKeyPair();
final pEncrypted = await p256.encrypt(
plaintext: 'Encrypted with P-256',
senderKeyPair: pSender,
receiverPublicKey: pReceiver.publicKey,
receiverNonce: pReceiver.nonce,
);
final pDecrypted = await p256.decrypt(
payload: pEncrypted,
receiverKeyPair: pReceiver,
);
print('P-256 result: $pDecrypted');
print('');
}
// ---------------------------------------------------------------------------
// 3. Healthcare context — encrypting a FHIR bundle for ABDM
// ---------------------------------------------------------------------------
Future<void> healthcareExample() async {
print('=== 3. Healthcare: ABDM Health Data Encryption ===\n');
// In an ABDM flow:
// HIP (Health Information Provider) encrypts patient data
// HIU (Health Information User) decrypts after consent
const fidelius = FideliusCurve25519();
// HIP generates a key pair and shares publicKey + nonce with HIU
final hipKeys = await fidelius.generateKeyPair();
// HIU generates a key pair and shares publicKey + nonce with HIP
final hiuKeys = await fidelius.generateKeyPair();
// HIP encrypts a FHIR bundle
final fhirBundle = jsonEncode({
'resourceType': 'Bundle',
'type': 'collection',
'entry': [
{
'resource': {
'resourceType': 'Patient',
'name': [
{
'given': ['Rahul'],
'family': 'Sharma',
},
],
},
},
{
'resource': {
'resourceType': 'Observation',
'code': {
'coding': [
{'system': 'http://loinc.org', 'code': '8867-4'},
],
'text': 'Heart rate',
},
'valueQuantity': {'value': 72, 'unit': 'bpm'},
},
},
],
});
final encrypted = await fidelius.encrypt(
plaintext: fhirBundle,
senderKeyPair: hipKeys,
receiverPublicKey: hiuKeys.publicKey,
receiverNonce: hiuKeys.nonce,
);
print('FHIR bundle encrypted (${fhirBundle.length} chars)');
print('Payload JSON keys: ${encrypted.toJson().keys.join(', ')}');
// HIU decrypts with their key pair
final decrypted = await fidelius.decrypt(
payload: encrypted,
receiverKeyPair: hiuKeys,
);
final bundle = jsonDecode(decrypted!);
print('Decrypted bundle type: ${bundle['type']}');
print('Entries: ${(bundle['entry'] as List).length}');
print('');
}
// ---------------------------------------------------------------------------
// 4. Low-level primitives — step-by-step protocol execution
// ---------------------------------------------------------------------------
Future<void> lowLevelPrimitives() async {
print('=== 4. Low-Level Primitives ===\n');
// Generate key pairs using the primitive function
final senderKeys = await generateKeyPair(FideliusCurveType.curve25519);
final receiverKeys = await generateKeyPair(FideliusCurveType.curve25519);
// Step 1: Decode keys and derive shared secret
final senderPriv = decodeBase64Strict(senderKeys.privateKey, 'sender priv');
final receiverPub = decodeBase64Strict(
receiverKeys.publicKey,
'receiver pub',
);
final sharedSecret = await deriveSharedSecret(
privateKeyBytes: senderPriv,
publicKeyBytes: receiverPub,
curve: FideliusCurveType.curve25519,
);
print('Shared secret: ${sharedSecret.length} bytes');
// Step 2: XOR nonces into salt + IV
final senderNonce = decodeBase64Strict(senderKeys.nonce, 'sender nonce');
final receiverNonce = decodeBase64Strict(
receiverKeys.nonce,
'receiver nonce',
);
final (:salt, :iv) = xorAndSplitNonces(senderNonce, receiverNonce);
print('HKDF salt: ${salt.length} bytes, AES-GCM IV: ${iv.length} bytes');
// Step 3: HKDF key derivation
final aesKey = await deriveKey(sharedSecret: sharedSecret, salt: salt);
print('AES-256 key: ${aesKey.length} bytes');
// Step 4: AES-GCM encrypt
final plaintext = utf8.encode('Low-level encryption test');
final ciphertext = await aesGcmEncrypt(
plaintext: plaintext,
key: aesKey,
iv: iv,
);
print('Ciphertext + tag: ${ciphertext.length} bytes');
// Step 5: AES-GCM decrypt
final decrypted = await aesGcmDecrypt(
ciphertext: ciphertext,
key: aesKey,
iv: iv,
);
print('Decrypted: ${utf8.decode(decrypted!)}');
// DER encoding
final rawPub = decodeBase64Strict(senderKeys.publicKey, 'pub');
final der = encodeX509SpkiDer(rawPub, FideliusCurveType.curve25519);
print('DER-encoded public key: ${der.length} bytes');
final decoded = decodeX509SpkiDer(der);
print(
'Round-trip DER: ${decoded.length} bytes (matches raw: ${decoded.length == rawPub.length})',
);
print('');
}
// ---------------------------------------------------------------------------
// 5. JSON serialization — payload and key pair round-trip
// ---------------------------------------------------------------------------
Future<void> jsonSerialization() async {
print('=== 5. JSON Serialization ===\n');
const fidelius = FideliusCurve25519();
final sender = await fidelius.generateKeyPair();
final receiver = await fidelius.generateKeyPair();
// Key pair JSON (safe — excludes private key)
final pubJson = sender.toJson();
print('Public JSON keys: ${pubJson.keys.join(', ')}');
print('Has privateKey? ${pubJson.containsKey('privateKey')}');
// Full key pair JSON (includes private key — for local storage only)
final fullJson = sender.toJsonWithPrivateKey();
final restored = FideliusKeyPair.fromJson(fullJson);
print('Restored nonce matches: ${restored.nonce == sender.nonce}');
// Encrypted payload JSON
final encrypted = await fidelius.encrypt(
plaintext: 'Serialization test',
senderKeyPair: sender,
receiverPublicKey: receiver.publicKey,
receiverNonce: receiver.nonce,
);
final payloadJson = jsonEncode(encrypted.toJson());
print('Payload JSON: ${payloadJson.substring(0, 50)}...');
// Reconstruct from JSON
final parsed = FideliusEncryptedPayload.fromJson(
jsonDecode(payloadJson) as Map<String, dynamic>,
);
final decrypted = await fidelius.decrypt(
payload: parsed,
receiverKeyPair: receiver,
);
print('Decrypted from JSON: $decrypted');
print('');
}