594 lines
18 KiB
ReStructuredText
594 lines
18 KiB
ReStructuredText
CCC Cryptography Plugin — Milestone 3 App Integration
|
||
======================================================
|
||
|
||
:Created: 2026-03-16
|
||
:Status: Ready to Begin (API frozen; Windows + Linux tests deferred)
|
||
:Audience: LetUsMsg application team
|
||
:Plugin package: ``ccc_cryptography``
|
||
:Plugin version: ``0.0.1`` (pre-tag; ``v0.1.0`` tag pending)
|
||
:Milestone: 3 of 3
|
||
|
||
Overview
|
||
--------
|
||
|
||
This document describes how the **LetUsMsg** application integrates the
|
||
``ccc_cryptography`` Flutter plugin.
|
||
|
||
Milestone 2 delivered a fully tested Flutter/Dart crypto plugin backed by
|
||
``ccc_rust`` (wolfSSL). All 16 integration tests pass on macOS, iOS, and
|
||
Android. Windows and Linux platform builds are deferred but do not block
|
||
app integration — the Dart API is frozen.
|
||
|
||
**The single rule for Milestone 3:** consume the published Dart API only.
|
||
No Rust changes, no bridge changes, no modifications to the plugin source.
|
||
|
||
Related Documents
|
||
~~~~~~~~~~~~~~~~~
|
||
|
||
* ``docs/ccc_fplugin_phases.rst`` — Milestone 2 phase completion status
|
||
* ``docs/ccc_fplugin_architecture_design.rst`` — Plugin architecture
|
||
* ``docs/ccc_fplugin_build_windows.rst`` — Windows build guide (deferred)
|
||
|
||
----
|
||
|
||
Open Items (not blocking M3 start)
|
||
-----------------------------------
|
||
|
||
The following Milestone 2 tasks are open but do not affect the Dart API:
|
||
|
||
* ``flutter build windows`` / integration tests on Windows — deferred
|
||
* ``flutter build linux`` / integration tests on Linux — deferred
|
||
* ``CHANGELOG.md`` not yet updated
|
||
* ``ccc_rust`` dependency commit not yet pinned to the final ``v0.1.0`` tag
|
||
* Plugin not yet tagged ``v0.1.0``
|
||
|
||
These will be resolved in parallel. The app may begin integration against
|
||
the current commit. When the ``v0.1.0`` tag is cut, update the git ref in
|
||
the app's ``pubspec.yaml``.
|
||
|
||
----
|
||
|
||
Phase Overview
|
||
--------------
|
||
|
||
====== ============================================ ========== ============
|
||
Phase Title Status Depends on
|
||
====== ============================================ ========== ============
|
||
M3-1 Add plugin dependency Not started —
|
||
M3-2 App startup initialisation Not started M3-1
|
||
M3-3 Replace AEAD crypto stubs Not started M3-2
|
||
M3-4 Replace KDF stubs Not started M3-2
|
||
M3-5 Replace MAC stubs Not started M3-2
|
||
M3-6 Replace Hash stubs Not started M3-2
|
||
M3-7 Replace KEM stubs Not started M3-2
|
||
M3-8 Provider catalog integration Not started M3-2
|
||
M3-9 Self-test debug UI Not started M3-2
|
||
M3-10 Exception handling audit Not started M3-3–7
|
||
M3-11 End-to-end app testing Not started M3-3–9
|
||
====== ============================================ ========== ============
|
||
|
||
----
|
||
|
||
Phase M3-1 — Add Plugin Dependency
|
||
------------------------------------
|
||
|
||
Add the plugin to the LetUsMsg ``pubspec.yaml``::
|
||
|
||
dependencies:
|
||
ccc_cryptography:
|
||
git:
|
||
url: ssh://git@10.0.5.109/j3g/ccc_fplugin.git
|
||
ref: main # replace with "v0.1.0" once tagged
|
||
|
||
Then fetch::
|
||
|
||
flutter pub get
|
||
|
||
.. note::
|
||
The plugin is not published to pub.dev. It is consumed as a git
|
||
dependency. When the ``v0.1.0`` tag is cut, change ``ref: main``
|
||
to ``ref: v0.1.0`` and pin the dependency.
|
||
|
||
----
|
||
|
||
Phase M3-2 — App Startup Initialisation
|
||
-----------------------------------------
|
||
|
||
``CccCrypto.init()`` must be called **once** at application startup,
|
||
before any cryptographic operation. It registers the wolfSSL provider
|
||
and initialises the FRB runtime. It is safe to call multiple times
|
||
(idempotent), but should be called exactly once in practice.
|
||
|
||
Typical placement is in ``main()`` before ``runApp()``::
|
||
|
||
import 'package:ccc_cryptography/ccc_cryptography.dart';
|
||
|
||
Future<void> main() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
await CccCrypto.init();
|
||
runApp(const MyApp());
|
||
}
|
||
|
||
Exit criteria:
|
||
|
||
* ``CccCrypto.init()`` called before any crypto operation in the app
|
||
* No ``CccInternalError`` thrown at startup (provider registered OK)
|
||
|
||
----
|
||
|
||
Phase M3-3 — Replace AEAD Crypto Stubs
|
||
-----------------------------------------
|
||
|
||
Replace existing encrypt/decrypt stubs with ``CccCrypto.encryptAead``
|
||
and ``CccCrypto.decryptAead``.
|
||
|
||
API signatures
|
||
~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
// Encrypt — returns ciphertext with authentication tag appended
|
||
Future<Uint8List> CccCrypto.encryptAead({
|
||
required CccAeadAlgorithm algorithm,
|
||
required Uint8List key,
|
||
required Uint8List nonce,
|
||
required Uint8List plaintext,
|
||
Uint8List? aad, // optional associated data; defaults to empty
|
||
});
|
||
|
||
// Decrypt — strips and verifies tag internally
|
||
// Throws CccAuthenticationFailed if tag does not verify
|
||
Future<Uint8List> CccCrypto.decryptAead({
|
||
required CccAeadAlgorithm algorithm,
|
||
required Uint8List key,
|
||
required Uint8List nonce,
|
||
required Uint8List ciphertext, // ciphertext + tag as returned by encryptAead
|
||
Uint8List? aad,
|
||
});
|
||
|
||
Algorithm enum and key/nonce sizes
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
========================= ===== ======= ===== ========
|
||
``CccAeadAlgorithm`` ID Key Nonce Tag
|
||
========================= ===== ======= ===== ========
|
||
``aesGcm256`` 12 32 B 12 B 16 B
|
||
``chaCha20Poly1305`` 13 32 B 12 B 16 B
|
||
``xChaCha20Poly1305`` 14 32 B 24 B 16 B
|
||
``ascon128a`` 15 16 B 16 B 16 B
|
||
========================= ===== ======= ===== ========
|
||
|
||
.. important::
|
||
The returned ciphertext from ``encryptAead`` is
|
||
``len(plaintext) + 16 bytes`` (plaintext + 16-byte tag concatenated).
|
||
Pass the entire slice to ``decryptAead`` — do not strip the tag
|
||
manually.
|
||
|
||
Example
|
||
~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
final key = ... // 32 bytes, from KDF or secure RNG
|
||
final nonce = ... // 12 bytes, non-repeating per (key, message) pair
|
||
|
||
final ciphertext = await CccCrypto.encryptAead(
|
||
algorithm: CccAeadAlgorithm.aesGcm256,
|
||
key: key,
|
||
nonce: nonce,
|
||
plaintext: message,
|
||
);
|
||
|
||
final plaintext = await CccCrypto.decryptAead(
|
||
algorithm: CccAeadAlgorithm.aesGcm256,
|
||
key: key,
|
||
nonce: nonce,
|
||
ciphertext: ciphertext, // full slice including tag
|
||
);
|
||
|
||
----
|
||
|
||
Phase M3-4 — Replace KDF Stubs
|
||
---------------------------------
|
||
|
||
API signature
|
||
~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
Future<Uint8List> CccCrypto.deriveKey({
|
||
required CccKdfAlgorithm algorithm,
|
||
required Uint8List ikm, // input key material
|
||
Uint8List? salt, // optional; defaults to empty
|
||
Uint8List? info, // optional context; defaults to empty
|
||
required int length, // desired output length in bytes
|
||
});
|
||
|
||
Algorithm enum
|
||
~~~~~~~~~~~~~~
|
||
|
||
========================== ===== ==========
|
||
``CccKdfAlgorithm`` ID Notes
|
||
========================== ===== ==========
|
||
``sha256`` 1 HKDF-SHA256
|
||
``sha384`` 2 HKDF-SHA384
|
||
``sha512`` 3 HKDF-SHA512
|
||
``blake2b512`` 4 BLAKE2b-512 KDF
|
||
========================== ===== ==========
|
||
|
||
Example — derive a 32-byte session key via HKDF-SHA256
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
final sessionKey = await CccCrypto.deriveKey(
|
||
algorithm: CccKdfAlgorithm.sha256,
|
||
ikm: sharedSecret,
|
||
salt: sessionSalt,
|
||
info: Uint8List.fromList(utf8.encode('letusmsg-session-v1')),
|
||
length: 32,
|
||
);
|
||
|
||
----
|
||
|
||
Phase M3-5 — Replace MAC Stubs
|
||
---------------------------------
|
||
|
||
API signatures
|
||
~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
Future<Uint8List> CccCrypto.computeMac({
|
||
required CccMacAlgorithm algorithm,
|
||
required Uint8List key,
|
||
required Uint8List data,
|
||
});
|
||
|
||
// Returns true if valid, false otherwise — never throws for tampered data
|
||
Future<bool> CccCrypto.verifyMac({
|
||
required CccMacAlgorithm algorithm,
|
||
required Uint8List key,
|
||
required Uint8List data,
|
||
required Uint8List mac,
|
||
});
|
||
|
||
Algorithm enum
|
||
~~~~~~~~~~~~~~
|
||
|
||
======================== =====
|
||
``CccMacAlgorithm`` ID
|
||
======================== =====
|
||
``hmacSha256`` 30
|
||
``hmacSha512`` 32
|
||
``blake2bMac`` 33
|
||
======================== =====
|
||
|
||
.. note::
|
||
``verifyMac`` uses a constant-time comparison in Rust to prevent
|
||
timing attacks. Do not compare MAC tags in Dart.
|
||
|
||
----
|
||
|
||
Phase M3-6 — Replace Hash Stubs
|
||
----------------------------------
|
||
|
||
API signature
|
||
~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
Future<Uint8List> CccCrypto.hash({
|
||
required CccHashAlgorithm algorithm,
|
||
required Uint8List data,
|
||
});
|
||
|
||
Algorithm enum
|
||
~~~~~~~~~~~~~~
|
||
|
||
======================== =====
|
||
``CccHashAlgorithm`` ID
|
||
======================== =====
|
||
``sha256`` 40
|
||
``sha384`` 41
|
||
``sha512`` 42
|
||
``blake2b512`` 43
|
||
``sha3_256`` 44
|
||
======================== =====
|
||
|
||
----
|
||
|
||
Phase M3-7 — Replace KEM Stubs
|
||
---------------------------------
|
||
|
||
API signatures
|
||
~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
// Key generation
|
||
Future<CccKemKeyPair> CccCrypto.kemGenerateKeypair({
|
||
required CccKemAlgorithm algorithm,
|
||
});
|
||
// CccKemKeyPair { Uint8List publicKey; Uint8List privateKey; }
|
||
|
||
// Encapsulation — sender uses recipient's public key
|
||
Future<CccKemEncapResult> CccCrypto.kemEncapsulate({
|
||
required CccKemAlgorithm algorithm,
|
||
required Uint8List publicKey,
|
||
});
|
||
// CccKemEncapResult { Uint8List ciphertext; Uint8List sharedSecret; }
|
||
|
||
// Decapsulation — recipient recovers shared secret
|
||
Future<Uint8List> CccCrypto.kemDecapsulate({
|
||
required CccKemAlgorithm algorithm,
|
||
required Uint8List privateKey,
|
||
required Uint8List ciphertext,
|
||
});
|
||
|
||
Algorithm enum
|
||
~~~~~~~~~~~~~~
|
||
|
||
========================== ===== ==============================
|
||
``CccKemAlgorithm`` ID Notes
|
||
========================== ===== ==============================
|
||
``x25519`` 50 Available
|
||
``x448`` 51 Available
|
||
``mlKem768`` 52 Deferred (not compiled in v0.1)
|
||
``mlKem1024`` 53 Deferred
|
||
``classicMcEliece`` 54 Deferred
|
||
========================== ===== ==============================
|
||
|
||
.. warning::
|
||
``privateKey`` in ``CccKemKeyPair`` is Dart-side memory.
|
||
Discard it as soon as decapsulation is complete.
|
||
Do not persist or log private keys.
|
||
|
||
----
|
||
|
||
Phase M3-8 — Provider Catalog Integration
|
||
-------------------------------------------
|
||
|
||
``CccProviderCatalog`` exposes runtime algorithm availability so the
|
||
app can adapt its UI and protocol negotiation to what the provider
|
||
actually supports.
|
||
|
||
API
|
||
~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
// One-shot fetch; cache the result for the session lifetime
|
||
final catalog = await CccCrypto.getCapabilities();
|
||
|
||
catalog.providerName // "wolfssl"
|
||
catalog.aead // List<CccAlgorithmInfo>
|
||
catalog.availableAead // filtered: only available == true
|
||
catalog.kdf / .availableKdf
|
||
catalog.mac / .availableMac
|
||
catalog.hash / .availableHash
|
||
catalog.kem / .availableKem
|
||
|
||
// CccAlgorithmInfo fields:
|
||
// int id (matches enum discriminant)
|
||
// String name (e.g. "AES-256-GCM")
|
||
// bool available
|
||
// bool deterministicIo
|
||
// int efficiencyScore (0–100)
|
||
// int reliabilityScore (0–100)
|
||
|
||
Also available — synchronous provider list::
|
||
|
||
final List<String> providers = CccCrypto.listProviders();
|
||
// ["wolfssl"]
|
||
|
||
Suggested app usage
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
|
||
* Cache the ``CccProviderCatalog`` in a top-level service singleton
|
||
after ``CccCrypto.init()``.
|
||
* Use ``availableAead``, ``availableKem`` etc. to populate algorithm
|
||
selection UI and protocol negotiation logic.
|
||
* Use ``efficiencyScore`` / ``reliabilityScore`` to recommend a default
|
||
algorithm when the user has no preference.
|
||
|
||
----
|
||
|
||
Phase M3-9 — Self-Test Debug UI
|
||
----------------------------------
|
||
|
||
Expose ``CccCrypto.runSelfTest()`` in a developer/debug settings screen
|
||
to give field visibility into provider health.
|
||
|
||
API
|
||
~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
final CccSelfTestResult result = await CccCrypto.runSelfTest();
|
||
|
||
result.providerName // "wolfssl"
|
||
result.allPassed // bool — false if any algorithm fails
|
||
result.results // List<CccAlgorithmTestResult>
|
||
result.failures // filtered: only failed results
|
||
|
||
// CccAlgorithmTestResult fields:
|
||
// int algoId
|
||
// String algoName
|
||
// bool passed
|
||
// String? errorMessage // null on pass
|
||
|
||
Known caveat
|
||
~~~~~~~~~~~~
|
||
|
||
The ``ccc_rust`` AEAD self-test vectors currently miscount the output
|
||
length (they do not account for the appended 16-byte authentication tag).
|
||
This causes ``AES-256-GCM`` and ``ChaCha20-Poly1305`` to report failures
|
||
in the self-test report even though those algorithms work correctly.
|
||
This is a known upstream bug in ``ccc_rust`` and does not affect
|
||
production encrypt/decrypt operations. The debug UI should note this
|
||
when displaying AEAD self-test failures.
|
||
|
||
----
|
||
|
||
Phase M3-10 — Exception Handling Audit
|
||
-----------------------------------------
|
||
|
||
All ``CccCrypto`` methods throw typed subclasses of ``CccException``
|
||
(a ``sealed class``). The full hierarchy:
|
||
|
||
.. code-block:: dart
|
||
|
||
sealed class CccException implements Exception {
|
||
final String message;
|
||
}
|
||
|
||
class CccUnsupportedAlgorithm extends CccException { ... }
|
||
class CccInvalidKey extends CccException { ... }
|
||
class CccInvalidNonce extends CccException { ... }
|
||
class CccAuthenticationFailed extends CccException { ... }
|
||
class CccInvalidInput extends CccException { ... }
|
||
class CccFeatureNotCompiled extends CccException { ... }
|
||
class CccInternalError extends CccException { ... }
|
||
|
||
Handling guidelines
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
|
||
* Catch ``CccAuthenticationFailed`` specifically — it is the signal
|
||
for a tampered or corrupted message. Do not silently discard it.
|
||
* Catch ``CccUnsupportedAlgorithm`` at the protocol negotiation layer
|
||
to fall back gracefully.
|
||
* Catch ``CccInvalidKey`` / ``CccInvalidNonce`` at the key-management
|
||
layer — these indicate a programming error (wrong buffer size) and
|
||
should be surfaced in development, not silently swallowed.
|
||
* ``CccInternalError`` is unexpected; log it and surface a generic
|
||
"crypto failure" error to the user.
|
||
|
||
Dart 3 exhaustive switch example
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
.. code-block:: dart
|
||
|
||
try {
|
||
final ct = await CccCrypto.encryptAead(...);
|
||
} on CccException catch (e) {
|
||
switch (e) {
|
||
case CccAuthenticationFailed():
|
||
// message tampered
|
||
case CccInvalidKey():
|
||
// bad key — developer error
|
||
case CccInvalidNonce():
|
||
// bad nonce — developer error
|
||
case CccUnsupportedAlgorithm():
|
||
// negotiate fallback
|
||
case CccFeatureNotCompiled():
|
||
// algorithm not built in
|
||
case CccInvalidInput():
|
||
// bad input data
|
||
case CccInternalError():
|
||
// log and surface generic error
|
||
}
|
||
}
|
||
|
||
Audit checklist
|
||
~~~~~~~~~~~~~~~~
|
||
|
||
For every call-site that replaces an existing crypto stub, verify:
|
||
|
||
* ``CccAuthenticationFailed`` is caught and handled (not re-thrown as
|
||
a generic error)
|
||
* Key / nonce sizes match the table in Phase M3-3 (wrong sizes →
|
||
``CccInvalidKey`` / ``CccInvalidNonce``)
|
||
* No raw ``CccException`` is exposed to end-users — wrap in
|
||
app-level error types
|
||
|
||
----
|
||
|
||
Phase M3-11 — End-to-End App Testing
|
||
---------------------------------------
|
||
|
||
Run the existing LetUsMsg integration test suite after all stubs are
|
||
replaced to confirm no regressions.
|
||
|
||
Additionally verify:
|
||
|
||
* App startup completes with ``CccCrypto.init()`` — no crash or timeout
|
||
* Encrypt → transmit → decrypt roundtrip succeeds on iOS, Android, macOS
|
||
* Provider catalog loads and populates the settings UI correctly
|
||
* Self-test screen shows expected results (note AEAD caveat above)
|
||
* No ``CccException`` surfaces to end-users under normal operation
|
||
* Private key material is not logged or persisted anywhere in the app
|
||
|
||
----
|
||
|
||
Hard Boundaries for Milestone 3
|
||
---------------------------------
|
||
|
||
The following are **forbidden** during app integration:
|
||
|
||
* Modifying any file in the ``ccc_cryptography`` plugin repository
|
||
* Implementing cryptographic logic in Dart
|
||
* Calling ``ccc_rust`` or ``flutter_rust_bridge`` APIs directly
|
||
* Depending on internal generated files
|
||
(e.g. ``src/rust/api/crypto.dart``, ``frb_generated.dart``)
|
||
* Persisting or logging raw private keys (``CccKemKeyPair.privateKey``)
|
||
|
||
The app must depend **only** on the public barrel export::
|
||
|
||
import 'package:ccc_cryptography/ccc_cryptography.dart';
|
||
|
||
----
|
||
|
||
Algorithm ID Quick Reference
|
||
------------------------------
|
||
|
||
These IDs match the ``cipher_constants.dart`` integer constants in the
|
||
existing app exactly — no translation layer is needed.
|
||
|
||
========================= ===== ============================
|
||
Algorithm ID Category
|
||
========================= ===== ============================
|
||
``aesGcm256`` 12 AEAD
|
||
``chaCha20Poly1305`` 13 AEAD
|
||
``xChaCha20Poly1305`` 14 AEAD
|
||
``ascon128a`` 15 AEAD
|
||
``sha256`` (KDF) 1 KDF (HKDF-SHA256)
|
||
``sha384`` (KDF) 2 KDF (HKDF-SHA384)
|
||
``sha512`` (KDF) 3 KDF (HKDF-SHA512)
|
||
``blake2b512`` (KDF) 4 KDF
|
||
``hmacSha256`` 30 MAC
|
||
``hmacSha512`` 32 MAC
|
||
``blake2bMac`` 33 MAC
|
||
``sha256`` (Hash) 40 Hash
|
||
``sha384`` (Hash) 41 Hash
|
||
``sha512`` (Hash) 42 Hash
|
||
``blake2b512`` (Hash) 43 Hash
|
||
``sha3_256`` 44 Hash
|
||
``x25519`` 50 KEM
|
||
``x448`` 51 KEM
|
||
``mlKem768`` 52 KEM (deferred)
|
||
``mlKem1024`` 53 KEM (deferred)
|
||
``classicMcEliece`` 54 KEM (deferred)
|
||
========================= ===== ============================
|
||
|
||
----
|
||
|
||
Milestone 3 Completion Gate
|
||
----------------------------
|
||
|
||
*All items must be checked before Milestone 3 is considered complete.*
|
||
|
||
* ``[ ]`` Plugin added to LetUsMsg ``pubspec.yaml``
|
||
* ``[ ]`` ``CccCrypto.init()`` called at app startup
|
||
* ``[ ]`` All AEAD crypto stubs replaced (Phase M3-3)
|
||
* ``[ ]`` All KDF stubs replaced (Phase M3-4)
|
||
* ``[ ]`` All MAC stubs replaced (Phase M3-5)
|
||
* ``[ ]`` All Hash stubs replaced (Phase M3-6)
|
||
* ``[ ]`` All KEM stubs replaced (Phase M3-7)
|
||
* ``[ ]`` Provider catalog surface integrated (Phase M3-8)
|
||
* ``[ ]`` Self-test debug UI surface integrated (Phase M3-9)
|
||
* ``[ ]`` Exception handling audit complete (Phase M3-10)
|
||
* ``[ ]`` End-to-end app tests pass on iOS, Android, macOS (Phase M3-11)
|
||
* ``[ ]`` No raw private keys exposed, logged, or persisted
|
||
* ``[ ]`` Plugin dep pinned to ``v0.1.0`` git tag (when cut)
|