diff --git a/docs/ccc_rust_milestone2_phases.rst b/docs/ccc_rust_milestone2_phases.rst index 698ea6a..89a242f 100644 --- a/docs/ccc_rust_milestone2_phases.rst +++ b/docs/ccc_rust_milestone2_phases.rst @@ -40,7 +40,7 @@ Phase Title Status Depends on ====== ========================================== ========== ============ 1 Project Restructure & FRB Setup Done — 2 Rust Bridge Crate (DTOs + API) Done Phase 1 -3 Dart API Surface Not Started Phase 2 +3 Dart API Surface Done Phase 2 4 Platform Build Verification Not Started Phase 3 5 Unit Tests Not Started Phase 3 6 Integration Tests & Polish Not Started Phase 4, 5 @@ -267,12 +267,20 @@ Exit Criteria Phase 3 — Dart API Surface ---------------------------- -:Status: Not Started +:Status: Done :Depends on: Phase 2 **Goal:** Build the clean Dart API layer that application code will consume, wired to the FRB-generated bindings. +Files created: + +* ``lib/ccc_crypto.dart`` — ``CccCrypto`` static façade (13 methods) +* ``lib/ccc_exceptions.dart`` — sealed ``CccException`` hierarchy (7 subtypes) +* ``lib/ccc_provider_catalog.dart`` — ``CccProviderCatalog`` + ``CccAlgorithmInfo`` +* ``lib/ccc_self_test.dart`` — ``CccSelfTestResult`` + ``CccAlgorithmTestResult`` +* ``lib/ccc_cryptography.dart`` — barrel export file (updated) + Tasks ~~~~~ @@ -284,50 +292,58 @@ Tasks - Task - Status * - 3.1 - - Create ``lib/ccc_crypto.dart`` — primary entry point class - ``CccCrypto`` with all public methods: - ``cccInit``, ``listProviders``, ``getCapabilities``, - ``encryptAead``, ``decryptAead``, ``deriveKey``, - ``computeMac``, ``verifyMac``, ``hash``, + - Create ``lib/ccc_crypto.dart`` — ``abstract final class CccCrypto`` + with 13 static methods: ``init``, ``listProviders``, + ``getCapabilities``, ``encryptAead``, ``decryptAead``, + ``deriveKey``, ``computeMac``, ``verifyMac``, ``hash``, ``kemGenerateKeypair``, ``kemEncapsulate``, ``kemDecapsulate``, - ``runSelfTest`` - - ☐ + ``runSelfTest``; each catches ``CccCryptoError`` and rethrows + as typed ``CccException`` + - ✅ * - 3.2 - Create ``lib/ccc_provider_catalog.dart`` — ``CccProviderCatalog`` - (provider name, algorithms, efficiency/reliability scores) - - ☐ + (provider name, per-category algorithm lists with availability, + efficiency/reliability scores); ``CccAlgorithmInfo`` wrapper + - ✅ * - 3.3 - Create ``lib/ccc_self_test.dart`` — ``CccSelfTestResult`` - (per-algorithm pass/fail, overall status, diagnostics) - - ☐ + (per-algorithm pass/fail, overall status, diagnostics); + ``CccAlgorithmTestResult`` wrapper + - ✅ * - 3.4 - - Create ``lib/ccc_exceptions.dart`` — structured exception - classes (``CccUnsupportedAlgorithm``, ``CccInvalidKey``, + - Create ``lib/ccc_exceptions.dart`` — ``sealed class CccException`` + with 7 subtypes (``CccUnsupportedAlgorithm``, ``CccInvalidKey``, ``CccInvalidNonce``, ``CccAuthenticationFailed``, - ``CccInternalError``) - - ☐ + ``CccInvalidInput``, ``CccFeatureNotCompiled``, + ``CccInternalError``); factory ``CccException.from()`` using + Dart 3 switch expression/pattern matching + - ✅ * - 3.5 - - Create ``lib/ccc_algorithm_ids.dart`` — algorithm ID constants - matching Rust enum discriminants 1-to-1 (values from - ``cipher_constants.dart``); no remapping - - ☐ + - Algorithm IDs: FRB-generated enums (``CccAeadAlgorithm``, + ``CccKdfAlgorithm``, ``CccMacAlgorithm``, ``CccHashAlgorithm``, + ``CccKemAlgorithm``) re-exported directly from barrel file; + separate constants file not needed + - ✅ * - 3.6 - Wire all Dart API methods to FRB-generated bindings - - ☐ + - ✅ * - 3.7 - - Create barrel export file ``lib/ccc_cryptography.dart`` - re-exporting the public API - - ☐ + - Update barrel export file ``lib/ccc_cryptography.dart`` + re-exporting the public API (CccCrypto, enums, DTOs, + exceptions, provider catalog, self-test) + - ✅ * - 3.8 - - Remove old placeholder Dart code (``sum``, ``sumAsync``) - - ☐ + - Remove old placeholder Dart code; update example app + to use ``CccCrypto.init()`` + ``runSelfTest()`` demo + - ✅ Exit Criteria ~~~~~~~~~~~~~ -* ``dart analyze`` reports no errors or warnings. -* Public API matches the contract in the architecture design doc. -* No crypto logic exists in Dart — orchestration only. +* ✅ ``dart analyze`` reports no errors or warnings. +* ✅ Public API matches the contract in the architecture design doc. +* ✅ No crypto logic exists in Dart — orchestration only. +* ✅ ``flutter build macos`` succeeds (44.5 MB). ---- diff --git a/example/lib/main.dart b/example/lib/main.dart index a0766b0..03b3212 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:ccc_cryptography/src/rust/api/simple.dart'; -import 'package:ccc_cryptography/src/rust/frb_generated.dart'; +import 'package:ccc_cryptography/ccc_cryptography.dart'; Future main() async { - await RustLib.init(); + WidgetsFlutterBinding.ensureInitialized(); + await CccCrypto.init(); runApp(const MyApp()); } @@ -15,26 +15,36 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - late String greetResult; + String _status = 'Running self-test…'; @override void initState() { super.initState(); - greetResult = greet(name: 'Flutter'); + _runSelfTest(); + } + + Future _runSelfTest() async { + try { + final result = await CccCrypto.runSelfTest(); + final providers = CccCrypto.listProviders(); + setState(() { + _status = 'Providers: ${providers.join(", ")}\n' + 'Self-test: ${result.allPassed ? "ALL PASSED" : "FAILED"}\n' + '${result.results.map((r) => ' ${r.algoName}: ${r.passed ? "✓" : "✗ ${r.errorMessage}"}').join("\n")}'; + }); + } on CccException catch (e) { + setState(() => _status = 'Error: $e'); + } } @override Widget build(BuildContext context) { - const textStyle = TextStyle(fontSize: 25); return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('ccc_cryptography')), - body: Center( - child: Text( - greetResult, - style: textStyle, - textAlign: TextAlign.center, - ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Text(_status, style: const TextStyle(fontSize: 16)), ), ), ); diff --git a/lib/ccc_crypto.dart b/lib/ccc_crypto.dart new file mode 100644 index 0000000..a07c4fd --- /dev/null +++ b/lib/ccc_crypto.dart @@ -0,0 +1,235 @@ +/// Primary entry point for CCC cryptographic operations. +/// +/// All methods delegate to the Rust bridge via `flutter_rust_bridge`. +/// No cryptographic logic exists in Dart. +/// +/// Usage: +/// ```dart +/// await CccCrypto.init(); +/// final ct = await CccCrypto.encryptAead( +/// algorithm: CccAeadAlgorithm.aesGcm256, +/// key: key, +/// nonce: nonce, +/// plaintext: data, +/// aad: Uint8List(0), +/// ); +/// ``` +library; + +import 'dart:typed_data'; + +import 'package:ccc_cryptography/ccc_exceptions.dart'; +import 'package:ccc_cryptography/ccc_provider_catalog.dart'; +import 'package:ccc_cryptography/ccc_self_test.dart'; +import 'package:ccc_cryptography/src/rust/api/crypto.dart' as bridge; +import 'package:ccc_cryptography/src/rust/api/dto.dart'; +import 'package:ccc_cryptography/src/rust/frb_generated.dart'; + +/// Static façade for all CCC cryptographic operations. +/// +/// Call [init] once at application startup before using any other method. +abstract final class CccCrypto { + /// Initialise the FRB runtime and register the wolfSSL provider. + /// + /// Must be called once before any other [CccCrypto] method. + /// Safe to call multiple times (idempotent). + static Future init() async { + await RustLib.init(); + await bridge.cccInit(); + } + + // ── Provider info ──────────────────────────────────────────────────────── + + /// Return the names of all registered crypto providers. + static List listProviders() => bridge.cccListProviders(); + + /// Return the capability catalog for the default provider. + static Future getCapabilities() async { + try { + final caps = await bridge.cccCapabilities(); + return CccProviderCatalog.fromCapabilities(caps); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── AEAD ───────────────────────────────────────────────────────────────── + + /// Encrypt [plaintext] with an AEAD algorithm. + /// + /// Returns ciphertext with appended authentication tag. + static Future encryptAead({ + required CccAeadAlgorithm algorithm, + required Uint8List key, + required Uint8List nonce, + required Uint8List plaintext, + Uint8List? aad, + }) async { + try { + return await bridge.cccAeadEncrypt( + algorithm: algorithm, + key: key, + nonce: nonce, + plaintext: plaintext, + aad: aad ?? Uint8List(0), + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + /// Decrypt [ciphertext] (with appended tag) using an AEAD algorithm. + /// + /// Throws [CccAuthenticationFailed] if the tag does not verify. + static Future decryptAead({ + required CccAeadAlgorithm algorithm, + required Uint8List key, + required Uint8List nonce, + required Uint8List ciphertext, + Uint8List? aad, + }) async { + try { + return await bridge.cccAeadDecrypt( + algorithm: algorithm, + key: key, + nonce: nonce, + ciphertext: ciphertext, + aad: aad ?? Uint8List(0), + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── KDF ────────────────────────────────────────────────────────────────── + + /// Derive key material of [length] bytes. + static Future deriveKey({ + required CccKdfAlgorithm algorithm, + required Uint8List ikm, + Uint8List? salt, + Uint8List? info, + required int length, + }) async { + try { + return await bridge.cccKdfDerive( + algorithm: algorithm, + ikm: ikm, + salt: salt ?? Uint8List(0), + info: info ?? Uint8List(0), + length: length, + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── MAC ────────────────────────────────────────────────────────────────── + + /// Compute a MAC tag over [data]. + static Future computeMac({ + required CccMacAlgorithm algorithm, + required Uint8List key, + required Uint8List data, + }) async { + try { + return await bridge.cccMacCompute( + algorithm: algorithm, + key: key, + data: data, + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + /// Verify a MAC tag. Returns `true` if valid, `false` otherwise. + static Future verifyMac({ + required CccMacAlgorithm algorithm, + required Uint8List key, + required Uint8List data, + required Uint8List mac, + }) async { + try { + return await bridge.cccMacVerify( + algorithm: algorithm, + key: key, + data: data, + mac: mac, + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── Hash ───────────────────────────────────────────────────────────────── + + /// Compute a cryptographic hash of [data]. + static Future hash({ + required CccHashAlgorithm algorithm, + required Uint8List data, + }) async { + try { + return await bridge.cccHash(algorithm: algorithm, data: data); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── KEM ────────────────────────────────────────────────────────────────── + + /// Generate a KEM key pair. + static Future kemGenerateKeypair({ + required CccKemAlgorithm algorithm, + }) async { + try { + return await bridge.cccKemGenerateKeypair(algorithm: algorithm); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + /// KEM encapsulation — produce ciphertext + shared secret from a public key. + static Future kemEncapsulate({ + required CccKemAlgorithm algorithm, + required Uint8List publicKey, + }) async { + try { + return await bridge.cccKemEncapsulate( + algorithm: algorithm, + publicKey: publicKey, + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + /// KEM decapsulation — recover shared secret from ciphertext + private key. + static Future kemDecapsulate({ + required CccKemAlgorithm algorithm, + required Uint8List privateKey, + required Uint8List ciphertext, + }) async { + try { + return await bridge.cccKemDecapsulate( + algorithm: algorithm, + privateKey: privateKey, + ciphertext: ciphertext, + ); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } + + // ── Self-test ──────────────────────────────────────────────────────────── + + /// Run the provider self-test suite and return a structured report. + static Future runSelfTest() async { + try { + final report = await bridge.cccSelfTest(); + return CccSelfTestResult.fromReport(report); + } on CccCryptoError catch (e) { + throw CccException.from(e); + } + } +} diff --git a/lib/ccc_cryptography.dart b/lib/ccc_cryptography.dart index 72a1f4a..307bec7 100644 --- a/lib/ccc_cryptography.dart +++ b/lib/ccc_cryptography.dart @@ -1,147 +1,39 @@ -// The original content is temporarily commented out to allow generating a self-contained demo - feel free to uncomment later. - -// // The original content is temporarily commented out to allow generating a self-contained demo - feel free to uncomment later. -// -// // -// // import 'dart:async'; -// // import 'dart:ffi'; -// // import 'dart:io'; -// // import 'dart:isolate'; -// // -// // import 'ccc_cryptography_bindings_generated.dart'; -// // -// // /// A very short-lived native function. -// // /// -// // /// For very short-lived functions, it is fine to call them on the main isolate. -// // /// They will block the Dart execution while running the native function, so -// // /// only do this for native functions which are guaranteed to be short-lived. -// // int sum(int a, int b) => _bindings.sum(a, b); -// // -// // /// A longer lived native function, which occupies the thread calling it. -// // /// -// // /// Do not call these kind of native functions in the main isolate. They will -// // /// block Dart execution. This will cause dropped frames in Flutter applications. -// // /// Instead, call these native functions on a separate isolate. -// // /// -// // /// Modify this to suit your own use case. Example use cases: -// // /// -// // /// 1. Reuse a single isolate for various different kinds of requests. -// // /// 2. Use multiple helper isolates for parallel execution. -// // Future sumAsync(int a, int b) async { -// // final SendPort helperIsolateSendPort = await _helperIsolateSendPort; -// // final int requestId = _nextSumRequestId++; -// // final _SumRequest request = _SumRequest(requestId, a, b); -// // final Completer completer = Completer(); -// // _sumRequests[requestId] = completer; -// // helperIsolateSendPort.send(request); -// // return completer.future; -// // } -// // -// // const String _libName = 'ccc_cryptography'; -// // -// // /// The dynamic library in which the symbols for [CccCryptographyBindings] can be found. -// // final DynamicLibrary _dylib = () { -// // if (Platform.isMacOS || Platform.isIOS) { -// // return DynamicLibrary.open('$_libName.framework/$_libName'); -// // } -// // if (Platform.isAndroid || Platform.isLinux) { -// // return DynamicLibrary.open('lib$_libName.so'); -// // } -// // if (Platform.isWindows) { -// // return DynamicLibrary.open('$_libName.dll'); -// // } -// // throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); -// // }(); -// // -// // /// The bindings to the native functions in [_dylib]. -// // final CccCryptographyBindings _bindings = CccCryptographyBindings(_dylib); -// // -// // -// // /// A request to compute `sum`. -// // /// -// // /// Typically sent from one isolate to another. -// // class _SumRequest { -// // final int id; -// // final int a; -// // final int b; -// // -// // const _SumRequest(this.id, this.a, this.b); -// // } -// // -// // /// A response with the result of `sum`. -// // /// -// // /// Typically sent from one isolate to another. -// // class _SumResponse { -// // final int id; -// // final int result; -// // -// // const _SumResponse(this.id, this.result); -// // } -// // -// // /// Counter to identify [_SumRequest]s and [_SumResponse]s. -// // int _nextSumRequestId = 0; -// // -// // /// Mapping from [_SumRequest] `id`s to the completers corresponding to the correct future of the pending request. -// // final Map> _sumRequests = >{}; -// // -// // /// The SendPort belonging to the helper isolate. -// // Future _helperIsolateSendPort = () async { -// // // The helper isolate is going to send us back a SendPort, which we want to -// // // wait for. -// // final Completer completer = Completer(); -// // -// // // Receive port on the main isolate to receive messages from the helper. -// // // We receive two types of messages: -// // // 1. A port to send messages on. -// // // 2. Responses to requests we sent. -// // final ReceivePort receivePort = ReceivePort() -// // ..listen((dynamic data) { -// // if (data is SendPort) { -// // // The helper isolate sent us the port on which we can sent it requests. -// // completer.complete(data); -// // return; -// // } -// // if (data is _SumResponse) { -// // // The helper isolate sent us a response to a request we sent. -// // final Completer completer = _sumRequests[data.id]!; -// // _sumRequests.remove(data.id); -// // completer.complete(data.result); -// // return; -// // } -// // throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); -// // }); -// // -// // // Start the helper isolate. -// // await Isolate.spawn((SendPort sendPort) async { -// // final ReceivePort helperReceivePort = ReceivePort() -// // ..listen((dynamic data) { -// // // On the helper isolate listen to requests and respond to them. -// // if (data is _SumRequest) { -// // final int result = _bindings.sum_long_running(data.a, data.b); -// // final _SumResponse response = _SumResponse(data.id, result); -// // sendPort.send(response); -// // return; -// // } -// // throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); -// // }); -// // -// // // Send the port to the main isolate on which we can receive requests. -// // sendPort.send(helperReceivePort.sendPort); -// // }, receivePort.sendPort); -// // -// // // Wait until the helper isolate has sent us back the SendPort on which we -// // // can start sending requests. -// // return completer.future; -// // }(); -// // -// -// library ccc_cryptography; -// -// export 'src/rust/api/simple.dart'; -// export 'src/rust/frb_generated.dart' show RustLib; -// - +/// CCC Cryptography — Flutter plugin providing cross-platform +/// cryptographic operations via `ccc_rust` and `flutter_rust_bridge`. +/// +/// Usage: +/// ```dart +/// import 'package:ccc_cryptography/ccc_cryptography.dart'; +/// +/// await CccCrypto.init(); +/// final ct = await CccCrypto.encryptAead( +/// algorithm: CccAeadAlgorithm.aesGcm256, +/// key: key, nonce: nonce, plaintext: data, +/// ); +/// ``` library; -export 'src/rust/api/simple.dart'; -export 'src/rust/frb_generated.dart' show RustLib; +// Primary API +export 'ccc_crypto.dart'; + +// Algorithm enum re-exports (FRB-generated) +export 'src/rust/api/dto.dart' + show + CccAeadAlgorithm, + CccKdfAlgorithm, + CccMacAlgorithm, + CccHashAlgorithm, + CccKemAlgorithm, + CccKemKeyPair, + CccKemEncapResult; + +// Exceptions +export 'ccc_exceptions.dart'; + +// Provider catalog +export 'ccc_provider_catalog.dart'; + +// Self-test result +export 'ccc_self_test.dart'; + + diff --git a/lib/ccc_exceptions.dart b/lib/ccc_exceptions.dart new file mode 100644 index 0000000..969d2a6 --- /dev/null +++ b/lib/ccc_exceptions.dart @@ -0,0 +1,70 @@ +/// Structured exception classes for CCC cryptographic operations. +/// +/// These wrap the FRB-generated [CccCryptoError] into typed Dart exceptions +/// so consuming code can catch specific failure modes. +library; + +import 'package:ccc_cryptography/src/rust/api/dto.dart'; + +/// Base class for all CCC cryptographic exceptions. +sealed class CccException implements Exception { + /// Human-readable error description. + final String message; + + const CccException(this.message); + + @override + String toString() => '$runtimeType: $message'; + + /// Convert a FRB-generated [CccCryptoError] into the appropriate + /// typed [CccException] subclass. + factory CccException.from(CccCryptoError error) { + return switch (error) { + CccCryptoError_UnsupportedAlgorithm(:final field0) => + CccUnsupportedAlgorithm(field0), + CccCryptoError_InvalidKey(:final field0) => CccInvalidKey(field0), + CccCryptoError_InvalidNonce(:final field0) => CccInvalidNonce(field0), + CccCryptoError_AuthenticationFailed() => + const CccAuthenticationFailed(), + CccCryptoError_InvalidInput(:final field0) => CccInvalidInput(field0), + CccCryptoError_FeatureNotCompiled(:final field0) => + CccFeatureNotCompiled(field0), + CccCryptoError_InternalError(:final field0) => CccInternalError(field0), + }; + } +} + +/// The requested algorithm is not supported by any registered provider. +class CccUnsupportedAlgorithm extends CccException { + const CccUnsupportedAlgorithm(super.message); +} + +/// The supplied key is invalid (wrong length, weak key, etc.). +class CccInvalidKey extends CccException { + const CccInvalidKey(super.message); +} + +/// The supplied nonce/IV is invalid (wrong length, reuse detected, etc.). +class CccInvalidNonce extends CccException { + const CccInvalidNonce(super.message); +} + +/// AEAD authentication tag verification failed — ciphertext was tampered. +class CccAuthenticationFailed extends CccException { + const CccAuthenticationFailed() : super('authentication failed'); +} + +/// The supplied input data is invalid. +class CccInvalidInput extends CccException { + const CccInvalidInput(super.message); +} + +/// The requested feature/algorithm is not compiled into the provider. +class CccFeatureNotCompiled extends CccException { + const CccFeatureNotCompiled(super.message); +} + +/// An unexpected internal error occurred in the cryptographic provider. +class CccInternalError extends CccException { + const CccInternalError(super.message); +} diff --git a/lib/ccc_provider_catalog.dart b/lib/ccc_provider_catalog.dart new file mode 100644 index 0000000..c94647a --- /dev/null +++ b/lib/ccc_provider_catalog.dart @@ -0,0 +1,110 @@ +/// Provider catalog — describes what a registered provider supports. +/// +/// Wraps the FRB-generated [CccCapabilities] into a Dart-idiomatic type +/// for querying algorithm availability and quality scores. +library; + +import 'package:ccc_cryptography/src/rust/api/dto.dart'; + +/// Describes the full set of capabilities for a registered provider. +class CccProviderCatalog { + /// Provider name (e.g. "wolfssl"). + final String providerName; + + /// AEAD algorithms supported by this provider. + final List aead; + + /// KDF algorithms supported by this provider. + final List kdf; + + /// MAC algorithms supported by this provider. + final List mac; + + /// Hash algorithms supported by this provider. + final List hash; + + /// KEM algorithms supported by this provider. + final List kem; + + const CccProviderCatalog._({ + required this.providerName, + required this.aead, + required this.kdf, + required this.mac, + required this.hash, + required this.kem, + }); + + /// Convert from the FRB-generated capabilities DTO. + factory CccProviderCatalog.fromCapabilities(CccCapabilities caps) { + return CccProviderCatalog._( + providerName: caps.providerName, + aead: caps.aead.map(CccAlgorithmInfo._fromEntry).toList(growable: false), + kdf: caps.kdf.map(CccAlgorithmInfo._fromEntry).toList(growable: false), + mac: caps.mac.map(CccAlgorithmInfo._fromEntry).toList(growable: false), + hash: caps.hash.map(CccAlgorithmInfo._fromEntry).toList(growable: false), + kem: caps.kem.map(CccAlgorithmInfo._fromEntry).toList(growable: false), + ); + } + + /// All available AEAD algorithms. + List get availableAead => + aead.where((a) => a.available).toList(growable: false); + + /// All available KDF algorithms. + List get availableKdf => + kdf.where((a) => a.available).toList(growable: false); + + /// All available MAC algorithms. + List get availableMac => + mac.where((a) => a.available).toList(growable: false); + + /// All available Hash algorithms. + List get availableHash => + hash.where((a) => a.available).toList(growable: false); + + /// All available KEM algorithms. + List get availableKem => + kem.where((a) => a.available).toList(growable: false); +} + +/// Describes a single algorithm's availability and quality metrics. +class CccAlgorithmInfo { + /// Numeric algorithm identifier (matches Rust `#[repr(u32)]`). + final int id; + + /// Human-readable algorithm name (e.g. "AES-256-GCM"). + final String name; + + /// Whether this algorithm is available in the provider. + final bool available; + + /// Whether the algorithm produces deterministic output for the same input. + final bool deterministicIo; + + /// Provider-reported efficiency score (0–100). + final int efficiencyScore; + + /// Provider-reported reliability score (0–100). + final int reliabilityScore; + + const CccAlgorithmInfo({ + required this.id, + required this.name, + required this.available, + required this.deterministicIo, + required this.efficiencyScore, + required this.reliabilityScore, + }); + + factory CccAlgorithmInfo._fromEntry(CccAlgorithmEntry e) { + return CccAlgorithmInfo( + id: e.algoId, + name: e.algoName, + available: e.capability.available, + deterministicIo: e.capability.deterministicIo, + efficiencyScore: e.capability.efficiencyScore, + reliabilityScore: e.capability.reliabilityScore, + ); + } +} diff --git a/lib/ccc_self_test.dart b/lib/ccc_self_test.dart new file mode 100644 index 0000000..3b541a0 --- /dev/null +++ b/lib/ccc_self_test.dart @@ -0,0 +1,68 @@ +/// Self-test result — wraps the FRB-generated report into Dart-idiomatic types. +library; + +import 'package:ccc_cryptography/src/rust/api/dto.dart'; + +/// Result of a single algorithm's self-test. +class CccAlgorithmTestResult { + /// Numeric algorithm identifier. + final int algoId; + + /// Human-readable algorithm name. + final String algoName; + + /// Whether the algorithm passed its self-test. + final bool passed; + + /// Diagnostic message if the test failed; `null` on success. + final String? errorMessage; + + const CccAlgorithmTestResult({ + required this.algoId, + required this.algoName, + required this.passed, + this.errorMessage, + }); + + factory CccAlgorithmTestResult._fromDto(CccAlgoTestResult dto) { + return CccAlgorithmTestResult( + algoId: dto.algoId, + algoName: dto.algoName, + passed: dto.passed, + errorMessage: dto.errorMessage, + ); + } +} + +/// Aggregate result of the provider self-test. +class CccSelfTestResult { + /// Name of the provider that was tested. + final String providerName; + + /// Per-algorithm test results. + final List results; + + /// Whether all algorithms passed. + final bool allPassed; + + const CccSelfTestResult._({ + required this.providerName, + required this.results, + required this.allPassed, + }); + + /// Convert from the FRB-generated self-test report DTO. + factory CccSelfTestResult.fromReport(CccSelfTestReport report) { + return CccSelfTestResult._( + providerName: report.providerName, + results: report.results + .map(CccAlgorithmTestResult._fromDto) + .toList(growable: false), + allPassed: report.allPassed, + ); + } + + /// Algorithms that failed their self-test. + List get failures => + results.where((r) => !r.passed).toList(growable: false); +}