From bcbc9bea3db41ce6d6fbd6b998ffae244f60c32b Mon Sep 17 00:00:00 2001 From: Mike Kell Date: Sun, 9 Aug 2026 23:44:56 -0400 Subject: [PATCH] feat(crypto): complete Task 1.2 - Argon2id master key derivation and OS keychain bindings --- .gitignore | 13 + Cargo.lock | 506 ++++++++++++++++++ DECISION_LOG.md | 13 +- crates/chronogrid_crypto/Cargo.toml | 8 + crates/chronogrid_crypto/src/error.rs | 33 ++ crates/chronogrid_crypto/src/kdf.rs | 189 +++++++ crates/chronogrid_crypto/src/keychain.rs | 224 ++++++++ crates/chronogrid_crypto/src/lib.rs | 49 +- .../ADR/0002-crypto-argon2id-keychain.md | 38 ++ docs/TASKS/phase_1_storage_and_crdt.md | 2 +- target/.rustc_info.json | 1 - target/CACHEDIR.TAG | 3 - target/debug/.cargo-build-lock | 0 target/debug/.cargo-lock | 0 .../dep-lib-chronogrid_ai | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-chronogrid_ai | 1 - .../lib-chronogrid_ai.json | 1 - .../dep-lib-chronogrid_core | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-chronogrid_core | 1 - .../lib-chronogrid_core.json | 1 - .../dep-lib-chronogrid_crypto | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-chronogrid_crypto | 1 - .../lib-chronogrid_crypto.json | 1 - .../dep-lib-chronogrid_net | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-chronogrid_net | 1 - .../lib-chronogrid_net.json | 1 - .../deps/chronogrid_ai-866b27a55c7f8eb2.d | 5 - .../deps/chronogrid_core-4b25f3369283ba7e.d | 5 - .../deps/chronogrid_crypto-2743a56e71ece37d.d | 5 - .../deps/chronogrid_net-e3495be9d1f94e7e.d | 5 - .../libchronogrid_ai-866b27a55c7f8eb2.rmeta | Bin 1993 -> 0 bytes .../libchronogrid_core-4b25f3369283ba7e.rmeta | Bin 2021 -> 0 bytes ...ibchronogrid_crypto-2743a56e71ece37d.rmeta | Bin 1999 -> 0 bytes .../libchronogrid_net-e3495be9d1f94e7e.rmeta | Bin 2006 -> 0 bytes .../dep-graph.bin | Bin 12819 -> 0 bytes .../metadata.rmeta | Bin 1993 -> 0 bytes .../query-cache.bin | Bin 815 -> 0 bytes .../work-products.bin | Bin 100 -> 0 bytes .../s-hl79dsv8ja-0g58ob0.lock | 0 .../dep-graph.bin | Bin 12819 -> 0 bytes .../metadata.rmeta | Bin 2021 -> 0 bytes .../query-cache.bin | Bin 815 -> 0 bytes .../work-products.bin | Bin 100 -> 0 bytes .../s-hl79dsuzmm-00ggpwp.lock | 0 .../dep-graph.bin | Bin 12819 -> 0 bytes .../metadata.rmeta | Bin 1999 -> 0 bytes .../query-cache.bin | Bin 815 -> 0 bytes .../work-products.bin | Bin 100 -> 0 bytes .../s-hl79dsvfnw-1d37dg4.lock | 0 .../dep-graph.bin | Bin 12819 -> 0 bytes .../metadata.rmeta | Bin 2006 -> 0 bytes .../query-cache.bin | Bin 815 -> 0 bytes .../work-products.bin | Bin 100 -> 0 bytes .../s-hl79dsvk8o-0p6zmpn.lock | 0 58 files changed, 1064 insertions(+), 47 deletions(-) create mode 100644 .gitignore create mode 100644 crates/chronogrid_crypto/src/error.rs create mode 100644 crates/chronogrid_crypto/src/kdf.rs create mode 100644 crates/chronogrid_crypto/src/keychain.rs create mode 100644 docs/ARCHITECTURE/ADR/0002-crypto-argon2id-keychain.md delete mode 100644 target/.rustc_info.json delete mode 100644 target/CACHEDIR.TAG delete mode 100644 target/debug/.cargo-build-lock delete mode 100644 target/debug/.cargo-lock delete mode 100644 target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/dep-lib-chronogrid_ai delete mode 100644 target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/invoked.timestamp delete mode 100644 target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai delete mode 100644 target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai.json delete mode 100644 target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/dep-lib-chronogrid_core delete mode 100644 target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/invoked.timestamp delete mode 100644 target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core delete mode 100644 target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core.json delete mode 100644 target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/dep-lib-chronogrid_crypto delete mode 100644 target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/invoked.timestamp delete mode 100644 target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto delete mode 100644 target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto.json delete mode 100644 target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/dep-lib-chronogrid_net delete mode 100644 target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/invoked.timestamp delete mode 100644 target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net delete mode 100644 target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net.json delete mode 100644 target/debug/deps/chronogrid_ai-866b27a55c7f8eb2.d delete mode 100644 target/debug/deps/chronogrid_core-4b25f3369283ba7e.d delete mode 100644 target/debug/deps/chronogrid_crypto-2743a56e71ece37d.d delete mode 100644 target/debug/deps/chronogrid_net-e3495be9d1f94e7e.d delete mode 100644 target/debug/deps/libchronogrid_ai-866b27a55c7f8eb2.rmeta delete mode 100644 target/debug/deps/libchronogrid_core-4b25f3369283ba7e.rmeta delete mode 100644 target/debug/deps/libchronogrid_crypto-2743a56e71ece37d.rmeta delete mode 100644 target/debug/deps/libchronogrid_net-e3495be9d1f94e7e.rmeta delete mode 100644 target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/dep-graph.bin delete mode 100644 target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/metadata.rmeta delete mode 100644 target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/query-cache.bin delete mode 100644 target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/work-products.bin delete mode 100644 target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0.lock delete mode 100644 target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/dep-graph.bin delete mode 100644 target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/metadata.rmeta delete mode 100644 target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/query-cache.bin delete mode 100644 target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/work-products.bin delete mode 100644 target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp.lock delete mode 100644 target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/dep-graph.bin delete mode 100644 target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/metadata.rmeta delete mode 100644 target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/query-cache.bin delete mode 100644 target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/work-products.bin delete mode 100644 target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4.lock delete mode 100644 target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/dep-graph.bin delete mode 100644 target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/metadata.rmeta delete mode 100644 target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/query-cache.bin delete mode 100644 target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/work-products.bin delete mode 100644 target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a4d386 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Rust build artifacts +/target/ +**/*.rs.bk + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/Cargo.lock b/Cargo.lock index 5243e34..847b69c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,60 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + [[package]] name = "chronogrid_ai" version = "0.1.0" @@ -13,7 +67,459 @@ version = "0.1.0" [[package]] name = "chronogrid_crypto" version = "0.1.0" +dependencies = [ + "argon2", + "keyring", + "rand", + "thiserror", + "zeroize", +] [[package]] name = "chronogrid_net" version = "0.1.0" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "dbus", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "byteorder", + "dbus-secret-service", + "log", + "security-framework 2.11.1", + "security-framework 3.7.0", + "windows-sys 0.60.2", + "zeroize", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] diff --git a/DECISION_LOG.md b/DECISION_LOG.md index 65a317b..ebdfaa9 100644 --- a/DECISION_LOG.md +++ b/DECISION_LOG.md @@ -5,4 +5,15 @@ - **Status**: Accepted - **Context**: Design a multi-platform (Windows, Linux, Android, Headless) productivity engine for time, project, and note management without centralized servers. - **Decision**: Adopted Rust core with SQLCipher local storage, Automerge CRDT delta sync over libp2p, and Flutter presentation layer bound via C-FFI. - \ No newline at end of file + +## [2026-08-09] - ADL-002: chronogrid_crypto Crate – Argon2id KDF & OS Keychain + +- **Status**: Accepted +- **Context**: Task 1.2 required implementing the `chronogrid_crypto` crate with Argon2id master-key derivation and OS-native keychain bindings per `docs/ARCHITECTURE/02-security-and-crypto.md`. +- **Decision**: + - Argon2id parameters pinned: 64 MiB memory, 3 iterations, 4 threads, 16-byte random salt, 32-byte output. + - `DerivedKey` struct implements `Zeroize + Drop` to scrub sensitive memory on scope exit. + - OS keychain abstracted via `keyring` crate v3 (Windows Credential Manager / DPAPI, Linux SecretService / libsecret). Android keychain is excluded (`cfg(not(target_os = "android"))`) and will be handled by Flutter KeyStore bridge in Task 1.5. + - Raw key material is Base64-encoded before credential-store insertion for UTF-8 backend compatibility. + - Inline Base64 encode/decode helpers avoid adding an external Base64 dependency. + diff --git a/crates/chronogrid_crypto/Cargo.toml b/crates/chronogrid_crypto/Cargo.toml index 3c3651f..fbf96a1 100644 --- a/crates/chronogrid_crypto/Cargo.toml +++ b/crates/chronogrid_crypto/Cargo.toml @@ -7,5 +7,13 @@ rust-version.workspace = true publish = false [dependencies] +argon2 = "0.5" +rand = "0.8" +thiserror = "2" +zeroize = { version = "1", features = ["derive"] } + +# OS keychain support (not available on Android – handled via Flutter/NDK bridge) +[target.'cfg(not(target_os = "android"))'.dependencies] +keyring = { version = "3", features = ["sync-secret-service", "windows-native", "apple-native"] } [dev-dependencies] diff --git a/crates/chronogrid_crypto/src/error.rs b/crates/chronogrid_crypto/src/error.rs new file mode 100644 index 0000000..2d5ff20 --- /dev/null +++ b/crates/chronogrid_crypto/src/error.rs @@ -0,0 +1,33 @@ +//! Error types for the `chronogrid_crypto` crate. + +use thiserror::Error; + +/// Unified error type for all cryptographic operations. +#[derive(Debug, Error)] +pub enum CryptoError { + /// Argon2id hashing failed internally. + #[error("Argon2id key derivation failed: {0}")] + KeyDerivation(String), + + /// The provided salt has an invalid length (expected 16 bytes). + #[error("invalid salt length: expected 16 bytes, got {0}")] + InvalidSaltLength(usize), + + /// OS keychain operation failed. + #[error("keychain error: {0}")] + Keychain(String), + + /// The requested credential was not found in the OS keychain. + #[error("credential not found in keychain for service='{service}', account='{account}'")] + CredentialNotFound { + service: String, + account: String, + }, + + /// A generic / unexpected internal error. + #[error("internal crypto error: {0}")] + Internal(String), +} + +/// Convenience alias used throughout the crate. +pub type Result = std::result::Result; diff --git a/crates/chronogrid_crypto/src/kdf.rs b/crates/chronogrid_crypto/src/kdf.rs new file mode 100644 index 0000000..ae62ac2 --- /dev/null +++ b/crates/chronogrid_crypto/src/kdf.rs @@ -0,0 +1,189 @@ +//! Argon2id master-key derivation. +//! +//! Parameters are pinned to the values mandated by +//! `docs/ARCHITECTURE/02-security-and-crypto.md`: +//! +//! | Parameter | Value | +//! |-------------|---------------| +//! | Memory | 64 MiB (65 536 KiB) | +//! | Time cost | 3 iterations | +//! | Parallelism | 4 threads | +//! | Salt | 16 random bytes | +//! | Output | 32 bytes (256-bit master key) | + +use argon2::{Algorithm, Argon2, Params, Version}; +use rand::RngCore; +use zeroize::Zeroize; + +use crate::error::{CryptoError, Result}; + +// ── Spec-mandated constants ────────────────────────────────────────────────── + +/// Memory cost in KiB (64 MiB). +const ARGON2_MEMORY_KIB: u32 = 65_536; + +/// Time cost (iterations). +const ARGON2_TIME_COST: u32 = 3; + +/// Degree of parallelism. +const ARGON2_PARALLELISM: u32 = 4; + +/// Length of the derived key in bytes. +pub const DERIVED_KEY_LEN: usize = 32; + +/// Required salt length in bytes. +pub const SALT_LEN: usize = 16; + +// ── Public types ───────────────────────────────────────────────────────────── + +/// Container for a derived master key and its associated salt. +/// +/// Implements [`Zeroize`] + [`Drop`] so sensitive bytes are wiped from memory +/// when the value goes out of scope. +#[derive(Zeroize)] +#[zeroize(drop)] +pub struct DerivedKey { + /// 32-byte derived master key. + key: [u8; DERIVED_KEY_LEN], + /// 16-byte salt that was used during derivation. + salt: [u8; SALT_LEN], +} + +impl DerivedKey { + /// Returns the derived 32-byte key material. + #[inline] + pub fn key(&self) -> &[u8; DERIVED_KEY_LEN] { + &self.key + } + + /// Returns the 16-byte salt used during derivation. + #[inline] + pub fn salt(&self) -> &[u8; SALT_LEN] { + &self.salt + } + + /// Consumes self and returns the raw key bytes. + /// + /// **Warning**: The caller is responsible for zeroing these bytes after use. + #[inline] + pub fn into_key_bytes(self) -> [u8; DERIVED_KEY_LEN] { + let k = self.key; + // Prevent the Drop impl from double-zeroing by forgetting self. + std::mem::forget(self); + k + } +} + +impl std::fmt::Debug for DerivedKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DerivedKey") + .field("key", &"[REDACTED]") + .field("salt_len", &self.salt.len()) + .finish() + } +} + +// ── Core derivation function ───────────────────────────────────────────────── + +/// Generates a cryptographically random 16-byte salt. +pub fn generate_salt() -> [u8; SALT_LEN] { + let mut salt = [0u8; SALT_LEN]; + rand::thread_rng().fill_bytes(&mut salt); + salt +} + +/// Derives a 256-bit master key from `passphrase` using Argon2id. +/// +/// If `salt` is `None`, a fresh 16-byte random salt is generated. +/// If `salt` is `Some(bytes)`, the provided slice **must** be exactly 16 bytes. +/// +/// # Errors +/// +/// Returns [`CryptoError::InvalidSaltLength`] if a provided salt is not 16 bytes. +/// Returns [`CryptoError::KeyDerivation`] if the Argon2 hasher fails internally. +pub fn derive_master_key(passphrase: &str, salt: Option<&[u8]>) -> Result { + // Resolve or generate salt. + let salt_bytes: [u8; SALT_LEN] = match salt { + Some(s) => { + if s.len() != SALT_LEN { + return Err(CryptoError::InvalidSaltLength(s.len())); + } + let mut arr = [0u8; SALT_LEN]; + arr.copy_from_slice(s); + arr + } + None => generate_salt(), + }; + + // Build Argon2id with spec parameters. + let params = Params::new( + ARGON2_MEMORY_KIB, + ARGON2_TIME_COST, + ARGON2_PARALLELISM, + Some(DERIVED_KEY_LEN), + ) + .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?; + + let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params); + + // Derive key into output buffer. + let mut key_buf = [0u8; DERIVED_KEY_LEN]; + argon2 + .hash_password_into(passphrase.as_bytes(), &salt_bytes, &mut key_buf) + .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?; + + Ok(DerivedKey { + key: key_buf, + salt: salt_bytes, + }) +} + +// ── Tests ──────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derive_key_with_random_salt() { + let dk = derive_master_key("correct horse battery staple", None) + .expect("derivation should succeed"); + assert_eq!(dk.key().len(), DERIVED_KEY_LEN); + assert_eq!(dk.salt().len(), SALT_LEN); + } + + #[test] + fn derive_key_deterministic_with_same_salt() { + let salt = [0xABu8; SALT_LEN]; + let dk1 = derive_master_key("hunter2", Some(&salt)).unwrap(); + let dk2 = derive_master_key("hunter2", Some(&salt)).unwrap(); + assert_eq!(dk1.key(), dk2.key(), "same passphrase + salt must yield identical key"); + } + + #[test] + fn different_passphrases_yield_different_keys() { + let salt = [0x01u8; SALT_LEN]; + let dk1 = derive_master_key("alpha", Some(&salt)).unwrap(); + let dk2 = derive_master_key("beta", Some(&salt)).unwrap(); + assert_ne!(dk1.key(), dk2.key()); + } + + #[test] + fn reject_invalid_salt_length() { + let bad_salt = [0u8; 10]; + let res = derive_master_key("pw", Some(&bad_salt)); + assert!(res.is_err()); + match res.unwrap_err() { + CryptoError::InvalidSaltLength(len) => assert_eq!(len, 10), + other => panic!("expected InvalidSaltLength, got: {other}"), + } + } + + #[test] + fn debug_does_not_leak_key() { + let dk = derive_master_key("secret", None).unwrap(); + let debug = format!("{dk:?}"); + assert!(debug.contains("REDACTED"), "Debug output must redact key material"); + assert!(!debug.contains("secret")); + } +} diff --git a/crates/chronogrid_crypto/src/keychain.rs b/crates/chronogrid_crypto/src/keychain.rs new file mode 100644 index 0000000..6826ced --- /dev/null +++ b/crates/chronogrid_crypto/src/keychain.rs @@ -0,0 +1,224 @@ +//! OS keychain integration for secure credential storage. +//! +//! Abstracts platform-specific keychain backends: +//! +//! | Platform | Backend | +//! |----------|----------------------------------------------| +//! | Windows | Windows Credential Manager (DPAPI) | +//! | Linux | SecretService D-Bus API (libsecret) | +//! | macOS | macOS Keychain (Security framework) | +//! | Android | **Not supported here** — use Flutter KeyStore bridge | +//! +//! This module is compiled only on non-Android targets. + +#[cfg(not(target_os = "android"))] +mod platform { + use keyring::Entry; + + use crate::error::{CryptoError, Result}; + + /// Default service identifier used when none is provided. + pub const DEFAULT_SERVICE: &str = "com.chronogrid.p2p"; + + /// Default account identifier used when none is provided. + pub const DEFAULT_ACCOUNT: &str = "master_key"; + + /// Stores raw key material in the OS credential store. + /// + /// The bytes are Base64-encoded before storage because some credential + /// backends only support UTF-8 string payloads. + /// + /// # Arguments + /// + /// * `service` – Logical service name (e.g. `"com.chronogrid.p2p"`). + /// * `account` – Account / label for the credential. + /// * `key_material` – Raw bytes to store (typically 32-byte master key + 16-byte salt). + pub fn store_credential( + service: &str, + account: &str, + key_material: &[u8], + ) -> Result<()> { + let entry = Entry::new(service, account) + .map_err(|e| CryptoError::Keychain(e.to_string()))?; + + // Encode as Base64 for credential-manager compatibility. + use base64_encode::encode; + let encoded = encode(key_material); + + entry + .set_password(&encoded) + .map_err(|e| CryptoError::Keychain(e.to_string()))?; + + Ok(()) + } + + /// Retrieves raw key material from the OS credential store. + /// + /// # Errors + /// + /// Returns [`CryptoError::CredentialNotFound`] when no matching entry exists. + pub fn retrieve_credential(service: &str, account: &str) -> Result> { + let entry = Entry::new(service, account) + .map_err(|e| CryptoError::Keychain(e.to_string()))?; + + let encoded = entry.get_password().map_err(|e| { + // Distinguish "not found" from other failures. + let msg = e.to_string(); + if msg.contains("No matching entry") + || msg.contains("not found") + || msg.contains("NoEntry") + { + CryptoError::CredentialNotFound { + service: service.to_owned(), + account: account.to_owned(), + } + } else { + CryptoError::Keychain(msg) + } + })?; + + use base64_decode::decode; + let bytes = decode(&encoded) + .map_err(|e| CryptoError::Internal(format!("base64 decode failed: {e}")))?; + + Ok(bytes) + } + + /// Deletes a credential from the OS credential store. + /// + /// Returns `Ok(())` even if the credential did not exist (idempotent). + pub fn delete_credential(service: &str, account: &str) -> Result<()> { + let entry = Entry::new(service, account) + .map_err(|e| CryptoError::Keychain(e.to_string()))?; + + match entry.delete_credential() { + Ok(()) => Ok(()), + Err(e) => { + let msg = e.to_string(); + // Treat "not found" as a no-op for idempotency. + if msg.contains("No matching entry") + || msg.contains("not found") + || msg.contains("NoEntry") + { + Ok(()) + } else { + Err(CryptoError::Keychain(msg)) + } + } + } + } + + // ── Inline Base64 helpers (no extra dependency needed) ──────────────── + + mod base64_encode { + use std::fmt::Write; + + const CHARS: &[u8; 64] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + pub fn encode(input: &[u8]) -> String { + let mut out = String::with_capacity((input.len() + 2) / 3 * 4); + for chunk in input.chunks(3) { + let b0 = chunk[0] as u32; + let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 }; + let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 }; + let triple = (b0 << 16) | (b1 << 8) | b2; + + let _ = write!(out, "{}", CHARS[((triple >> 18) & 0x3F) as usize] as char); + let _ = write!(out, "{}", CHARS[((triple >> 12) & 0x3F) as usize] as char); + if chunk.len() > 1 { + let _ = write!(out, "{}", CHARS[((triple >> 6) & 0x3F) as usize] as char); + } else { + out.push('='); + } + if chunk.len() > 2 { + let _ = write!(out, "{}", CHARS[(triple & 0x3F) as usize] as char); + } else { + out.push('='); + } + } + out + } + } + + mod base64_decode { + pub fn decode(input: &str) -> std::result::Result, String> { + let input = input.trim_end_matches('='); + let mut out = Vec::with_capacity(input.len() * 3 / 4); + + let val = |c: u8| -> std::result::Result { + match c { + b'A'..=b'Z' => Ok((c - b'A') as u32), + b'a'..=b'z' => Ok((c - b'a' + 26) as u32), + b'0'..=b'9' => Ok((c - b'0' + 52) as u32), + b'+' => Ok(62), + b'/' => Ok(63), + _ => Err(format!("invalid base64 char: {c}")), + } + }; + + let bytes = input.as_bytes(); + let mut i = 0; + while i < bytes.len() { + let remaining = bytes.len() - i; + if remaining >= 4 { + let n = (val(bytes[i])? << 18) + | (val(bytes[i + 1])? << 12) + | (val(bytes[i + 2])? << 6) + | val(bytes[i + 3])?; + out.push((n >> 16) as u8); + out.push((n >> 8 & 0xFF) as u8); + out.push((n & 0xFF) as u8); + i += 4; + } else if remaining == 3 { + let n = (val(bytes[i])? << 18) + | (val(bytes[i + 1])? << 12) + | (val(bytes[i + 2])? << 6); + out.push((n >> 16) as u8); + out.push((n >> 8 & 0xFF) as u8); + i += 3; + } else if remaining == 2 { + let n = (val(bytes[i])? << 18) | (val(bytes[i + 1])? << 12); + out.push((n >> 16) as u8); + i += 2; + } else { + break; + } + } + + Ok(out) + } + } + + #[cfg(test)] + mod tests { + use super::base64_decode::decode; + use super::base64_encode::encode; + + #[test] + fn base64_round_trip() { + let original = b"hello, chronogrid crypto!"; + let encoded = encode(original); + let decoded = decode(&encoded).expect("decode should succeed"); + assert_eq!(decoded, original); + } + + #[test] + fn base64_round_trip_32_bytes() { + let original: Vec = (0..32).collect(); + let encoded = encode(&original); + let decoded = decode(&encoded).expect("decode should succeed"); + assert_eq!(decoded, original); + } + + #[test] + fn base64_empty() { + assert_eq!(encode(b""), ""); + assert_eq!(decode("").unwrap(), Vec::::new()); + } + } +} + +// Re-export platform items at crate level (non-Android only). +#[cfg(not(target_os = "android"))] +pub use platform::*; diff --git a/crates/chronogrid_crypto/src/lib.rs b/crates/chronogrid_crypto/src/lib.rs index ea26eb5..32bb845 100644 --- a/crates/chronogrid_crypto/src/lib.rs +++ b/crates/chronogrid_crypto/src/lib.rs @@ -1,12 +1,43 @@ //! # chronogrid_crypto //! -//! Cryptographic primitives for ChronoGrid P2P. -//! Handles Argon2id master key derivation and OS keychain bindings. +//! Cryptographic primitives for **ChronoGrid P2P**. +//! +//! This crate provides two core capabilities: +//! +//! 1. **Argon2id master-key derivation** ([`kdf`]) — derives a 256-bit key from a +//! user passphrase using the parameters specified in +//! `docs/ARCHITECTURE/02-security-and-crypto.md`. +//! +//! 2. **OS keychain integration** ([`keychain`]) — stores and retrieves encrypted +//! key material via platform-native credential managers (Windows Credential +//! Manager / DPAPI, Linux SecretService / libsecret). Android credential +//! management is delegated to the Flutter KeyStore bridge. +//! +//! ## Quick Start +//! +//! ```rust,no_run +//! use chronogrid_crypto::{kdf, keychain}; +//! +//! // Derive a master key from a passphrase +//! let derived = kdf::derive_master_key("my secret passphrase", None) +//! .expect("key derivation failed"); +//! +//! // Persist the key + salt in the OS keychain +//! let mut combined = Vec::with_capacity(48); +//! combined.extend_from_slice(derived.key()); +//! combined.extend_from_slice(derived.salt()); +//! +//! keychain::store_credential( +//! keychain::DEFAULT_SERVICE, +//! keychain::DEFAULT_ACCOUNT, +//! &combined, +//! ).expect("keychain store failed"); +//! ``` -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert!(true); - } -} +pub mod error; +pub mod kdf; +pub mod keychain; + +// Re-export key public types at crate root for ergonomic imports. +pub use error::{CryptoError, Result}; +pub use kdf::{derive_master_key, generate_salt, DerivedKey, DERIVED_KEY_LEN, SALT_LEN}; diff --git a/docs/ARCHITECTURE/ADR/0002-crypto-argon2id-keychain.md b/docs/ARCHITECTURE/ADR/0002-crypto-argon2id-keychain.md new file mode 100644 index 0000000..c87f9e9 --- /dev/null +++ b/docs/ARCHITECTURE/ADR/0002-crypto-argon2id-keychain.md @@ -0,0 +1,38 @@ +# ADR-0002: chronogrid_crypto – Argon2id KDF & OS Keychain Strategy + +* **Status**: Accepted +* **Date**: 2026-08-09 +* **Deciders**: Michael Kell + +## Context & Problem Statement + +Task 1.2 requires implementing a `chronogrid_crypto` crate that provides: +1. A master-key derivation function using Argon2id with parameters mandated by `docs/ARCHITECTURE/02-security-and-crypto.md`. +2. Cross-platform OS keychain bindings for secure credential persistence (Windows, Linux). Android is deferred to the Flutter KeyStore bridge. + +## Options Considered + +1. **Hand-roll Argon2id via raw `blake2` + memory-hard loop** — maximum control but high risk of implementation bugs in a security-critical path. +2. **Use the `argon2` crate (RustCrypto)** — well-audited, widely used, supports Argon2id with configurable parameters out of the box. +3. **Use `ring` or `sodiumoxide`** — neither provides Argon2id natively; would still need the `argon2` crate. + +For keychain: +1. **Direct Win32 / libsecret FFI** — full control, but significant per-platform maintenance burden. +2. **`keyring` crate** — abstracts Windows Credential Manager, Linux SecretService, and macOS Keychain behind a single API. + +## Decision Outcome + +**Chosen Options:** +- `argon2` crate v0.5 (RustCrypto) for KDF — spec-compliant, zero-copy output, and widely audited. +- `keyring` crate v3 for cross-platform OS keychain — minimal code, covers Windows + Linux targets. Android is excluded via `cfg(not(target_os = "android"))` and will be handled by the Flutter KeyStore bridge. + +**Key design choices:** +- Argon2id parameters: memory = 65 536 KiB (64 MiB), time_cost = 3, parallelism = 4, salt = 16 bytes, output = 32 bytes. +- `DerivedKey` struct wraps key + salt and implements `Zeroize + Drop` for automatic memory scrubbing. +- `Debug` impl on `DerivedKey` redacts key material to prevent accidental log leaks. +- Raw bytes are Base64-encoded before keychain insertion for UTF-8 credential backend compatibility, using inline encode/decode helpers (no extra dependency). + +## Consequences & Trade-offs + +* **Positive**: Spec-compliant crypto with secure memory handling; single cross-platform keychain API; 8 unit tests + 1 doc-test provide baseline confidence. +* **Negative**: `keyring` v3 does not support Android — Android keychain must be wired separately via Flutter platform channels (planned for Task 1.5). Inline Base64 is minimal but could be replaced by `base64` crate if other crates need it later. diff --git a/docs/TASKS/phase_1_storage_and_crdt.md b/docs/TASKS/phase_1_storage_and_crdt.md index 7e0a051..bf87387 100644 --- a/docs/TASKS/phase_1_storage_and_crdt.md +++ b/docs/TASKS/phase_1_storage_and_crdt.md @@ -3,7 +3,7 @@ ## Status: Ready for Agent Execution - [x] **Task 1.1**: Initialize Rust Cargo Workspace in crates/ and configure root Cargo.toml. -- [ ] **Task 1.2**: Implement chronogrid_crypto crate with Argon2id master key derivation and OS keychain bindings. +- [x] **Task 1.2**: Implement chronogrid_crypto crate with Argon2id master key derivation and OS keychain bindings. - [ ] **Task 1.3**: Implement chronogrid_core database initializers using SQLCipher 4.x matching docs/API_CONTRACTS/schema.sql. - [ ] **Task 1.4**: Integrate Automerge Rust crate into chronogrid_core for task and note CRDT register mutations. - [ ] **Task 1.5**: Generate lutter_rust_bridge FFI bindings bridging Rust storage methods to Dart. diff --git a/target/.rustc_info.json b/target/.rustc_info.json deleted file mode 100644 index bffbb9a..0000000 --- a/target/.rustc_info.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc_fingerprint":7332174474168670565,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\mtkel\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_primitive_alignment=\"128\"\ntarget_has_atomic_primitive_alignment=\"16\"\ntarget_has_atomic_primitive_alignment=\"32\"\ntarget_has_atomic_primitive_alignment=\"64\"\ntarget_has_atomic_primitive_alignment=\"8\"\ntarget_has_atomic_primitive_alignment=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"6487759729724009271":{"success":true,"status":"","code":0,"stdout":"rustc 1.97.1 (8bab26f4f 2026-07-14)\nbinary: rustc\ncommit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452\ncommit-date: 2026-07-14\nhost: x86_64-pc-windows-msvc\nrelease: 1.97.1\nLLVM version: 22.1.6\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/target/CACHEDIR.TAG b/target/CACHEDIR.TAG deleted file mode 100644 index 20d7c31..0000000 --- a/target/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/target/debug/.cargo-build-lock b/target/debug/.cargo-build-lock deleted file mode 100644 index e69de29..0000000 diff --git a/target/debug/.cargo-lock b/target/debug/.cargo-lock deleted file mode 100644 index e69de29..0000000 diff --git a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/dep-lib-chronogrid_ai b/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/dep-lib-chronogrid_ai deleted file mode 100644 index 02bca30b2ac874769893912718791f0b09ae9c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C^;r4GfA(g7$m{~07TIQ`v3p{ diff --git a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/invoked.timestamp b/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/invoked.timestamp deleted file mode 100644 index e00328d..0000000 --- a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai b/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai deleted file mode 100644 index 50d70b5..0000000 --- a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai +++ /dev/null @@ -1 +0,0 @@ -e14ca8334710f7f6 \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai.json b/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai.json deleted file mode 100644 index 83292d4..0000000 --- a/target/debug/.fingerprint/chronogrid_ai-866b27a55c7f8eb2/lib-chronogrid_ai.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":3720210673988096810,"features":"[]","declared_features":"[]","target":16581365484989365424,"profile":17672942494452627365,"path":16933786149472085738,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\chronogrid_ai-866b27a55c7f8eb2\\dep-lib-chronogrid_ai","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0} \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/dep-lib-chronogrid_core b/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/dep-lib-chronogrid_core deleted file mode 100644 index 02bca30b2ac874769893912718791f0b09ae9c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C^;r4GfA(g7$m{~07TIQ`v3p{ diff --git a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/invoked.timestamp b/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/invoked.timestamp deleted file mode 100644 index e00328d..0000000 --- a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core b/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core deleted file mode 100644 index 8efd338..0000000 --- a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core +++ /dev/null @@ -1 +0,0 @@ -2341cb588ef75a5d \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core.json b/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core.json deleted file mode 100644 index a7988d9..0000000 --- a/target/debug/.fingerprint/chronogrid_core-4b25f3369283ba7e/lib-chronogrid_core.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":3720210673988096810,"features":"[]","declared_features":"[]","target":11075585498958225338,"profile":17672942494452627365,"path":14862345418060161617,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\chronogrid_core-4b25f3369283ba7e\\dep-lib-chronogrid_core","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0} \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/dep-lib-chronogrid_crypto b/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/dep-lib-chronogrid_crypto deleted file mode 100644 index 02bca30b2ac874769893912718791f0b09ae9c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C^;r4GfA(g7$m{~07TIQ`v3p{ diff --git a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/invoked.timestamp b/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/invoked.timestamp deleted file mode 100644 index e00328d..0000000 --- a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto b/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto deleted file mode 100644 index c587177..0000000 --- a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto +++ /dev/null @@ -1 +0,0 @@ -8f4ec8434a3e5c1b \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto.json b/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto.json deleted file mode 100644 index d9d2718..0000000 --- a/target/debug/.fingerprint/chronogrid_crypto-2743a56e71ece37d/lib-chronogrid_crypto.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":3720210673988096810,"features":"[]","declared_features":"[]","target":610712738496679682,"profile":17672942494452627365,"path":2818304346370217794,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\chronogrid_crypto-2743a56e71ece37d\\dep-lib-chronogrid_crypto","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0} \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/dep-lib-chronogrid_net b/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/dep-lib-chronogrid_net deleted file mode 100644 index 02bca30b2ac874769893912718791f0b09ae9c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C^;r4GfA(g7$m{~07TIQ`v3p{ diff --git a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/invoked.timestamp b/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/invoked.timestamp deleted file mode 100644 index e00328d..0000000 --- a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net b/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net deleted file mode 100644 index d79b717..0000000 --- a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net +++ /dev/null @@ -1 +0,0 @@ -50ffcf0c1a7b99c8 \ No newline at end of file diff --git a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net.json b/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net.json deleted file mode 100644 index ff83f3a..0000000 --- a/target/debug/.fingerprint/chronogrid_net-e3495be9d1f94e7e/lib-chronogrid_net.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":3720210673988096810,"features":"[]","declared_features":"[]","target":8139598702224368756,"profile":17672942494452627365,"path":17680580845150480923,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\chronogrid_net-e3495be9d1f94e7e\\dep-lib-chronogrid_net","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0} \ No newline at end of file diff --git a/target/debug/deps/chronogrid_ai-866b27a55c7f8eb2.d b/target/debug/deps/chronogrid_ai-866b27a55c7f8eb2.d deleted file mode 100644 index bc549ea..0000000 --- a/target/debug/deps/chronogrid_ai-866b27a55c7f8eb2.d +++ /dev/null @@ -1,5 +0,0 @@ -C:\programming\chronogrid\target\debug\deps\chronogrid_ai-866b27a55c7f8eb2.d: crates\chronogrid_ai\src\lib.rs - -C:\programming\chronogrid\target\debug\deps\libchronogrid_ai-866b27a55c7f8eb2.rmeta: crates\chronogrid_ai\src\lib.rs - -crates\chronogrid_ai\src\lib.rs: diff --git a/target/debug/deps/chronogrid_core-4b25f3369283ba7e.d b/target/debug/deps/chronogrid_core-4b25f3369283ba7e.d deleted file mode 100644 index 17ef7a4..0000000 --- a/target/debug/deps/chronogrid_core-4b25f3369283ba7e.d +++ /dev/null @@ -1,5 +0,0 @@ -C:\programming\chronogrid\target\debug\deps\chronogrid_core-4b25f3369283ba7e.d: crates\chronogrid_core\src\lib.rs - -C:\programming\chronogrid\target\debug\deps\libchronogrid_core-4b25f3369283ba7e.rmeta: crates\chronogrid_core\src\lib.rs - -crates\chronogrid_core\src\lib.rs: diff --git a/target/debug/deps/chronogrid_crypto-2743a56e71ece37d.d b/target/debug/deps/chronogrid_crypto-2743a56e71ece37d.d deleted file mode 100644 index 3fc769f..0000000 --- a/target/debug/deps/chronogrid_crypto-2743a56e71ece37d.d +++ /dev/null @@ -1,5 +0,0 @@ -C:\programming\chronogrid\target\debug\deps\chronogrid_crypto-2743a56e71ece37d.d: crates\chronogrid_crypto\src\lib.rs - -C:\programming\chronogrid\target\debug\deps\libchronogrid_crypto-2743a56e71ece37d.rmeta: crates\chronogrid_crypto\src\lib.rs - -crates\chronogrid_crypto\src\lib.rs: diff --git a/target/debug/deps/chronogrid_net-e3495be9d1f94e7e.d b/target/debug/deps/chronogrid_net-e3495be9d1f94e7e.d deleted file mode 100644 index ecdab69..0000000 --- a/target/debug/deps/chronogrid_net-e3495be9d1f94e7e.d +++ /dev/null @@ -1,5 +0,0 @@ -C:\programming\chronogrid\target\debug\deps\chronogrid_net-e3495be9d1f94e7e.d: crates\chronogrid_net\src\lib.rs - -C:\programming\chronogrid\target\debug\deps\libchronogrid_net-e3495be9d1f94e7e.rmeta: crates\chronogrid_net\src\lib.rs - -crates\chronogrid_net\src\lib.rs: diff --git a/target/debug/deps/libchronogrid_ai-866b27a55c7f8eb2.rmeta b/target/debug/deps/libchronogrid_ai-866b27a55c7f8eb2.rmeta deleted file mode 100644 index 3ad2fa265f9b3bddf2d6774f749446ff98d3f358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1993 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 diff --git a/target/debug/deps/libchronogrid_net-e3495be9d1f94e7e.rmeta b/target/debug/deps/libchronogrid_net-e3495be9d1f94e7e.rmeta deleted file mode 100644 index 3099f4423d40b37229d9627624545c0ecdb5e381..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2006 zcmb_ddu$X%7@wKl*0vly`T}YQPAJ79Tyy)l-MvCI*lJr0PzVHQ4=($dy`Eg}&fT-6 zh6GxC=a+q)IMqQX3fFyc zYJJgD$2RuuXZqw^!}Jlbw`#igKM^tt&8(dJyUc!4fGS8 z=M}>*n3`b*C=iTKZ#itIjBrQ7T45(uorLv-MV#JqC3V8t4~lz2le(9eOo_M-B)`EC z$>2CiQUyU19RTuMRl8NQ5*=|TZv!{^C9Ut!_E-4%&0k));#wGjWcU@y&(X+&ECguC z@*9OzW3hzpT>9jr!0CB2ckkMAclqqIukCX^Xo4&XoaxsTO%HH}8?(S&u0c{nPdOEf z4mU3Ezc6jquAx=qo|t;tH3^t9CrAos^15d5l0;1khl3iVO^rul#CfckQ4U`Hs_f3L zlC{gu|F*z2<2An+G*w;Hw4li;DqX9mBN{gn9dbPlz*9Bl*e2@* z*N6lHqN#8}MHfUx_KVagS2rW!sOdbLYm8675lg z*fJCR4bmFh}r}% zc>t5RZgnP8(L|hv0PeoL-(0sZ1ic8smWfAOPACPpuUsw6iZ|d*dk5d#U31^Xdv-$R zR~AvTUcl@G3@j<#!JI({n2#9%4l>)Bqv$hqoX*n>0gwy42)IeRp|}X|B;c)t5bYb~ z_NZjz?pWgd`SRi|PtqYYn*}V(RBfbiuX%}5_xsJ!vlk~4wdCsZAoQ#w78TH3XyN&JI5 zB`FxnOQJ8S`!W}Xugz}Pozf$lyM8IEe!O4${HU{T zU==7FnG8y7dV>gJFrbm3!I?pk$i%r;>fs`de$kUI2OMzEdPX)}D`1|>vGLi75t?z! zmZe~JhGFQ5$fx6wUKfx-3_^&00k3nOMgH$PeabQ`NA9{ZU*1~_gwR}sSo&uX{F$yj R;|K!xa`%zA;WMK#@;6fW^Jf46 diff --git a/target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/dep-graph.bin b/target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/dep-graph.bin deleted file mode 100644 index 8e94d2bb3362b61aaffa92fcd4de2e14f6a8f51a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12819 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 Bfe`=z diff --git a/target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/metadata.rmeta b/target/debug/incremental/chronogrid_ai-00kqg8as75teg/s-hl79dsv8ja-0g58ob0-0epgoeh8ku88a0dn63xx7hrnz/metadata.rmeta deleted file mode 100644 index 3ad2fa265f9b3bddf2d6774f749446ff98d3f358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1993 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 Ag#Z8m diff --git a/target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/work-products.bin b/target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp-d9bd3jjjqm25wm6tafs2r2vnj/work-products.bin deleted file mode 100644 index 4f769ac410b5283eb1fcd079ca2414c9da74232b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100 zcmWFv_H<@okTuk^G}kj!(6C5KOfoV{Gf7i0GB7gJH89sTG|^;SdF`;#7U#m3Ay@R* r*sD53apb0!B&H;mBpzgBEdsF*^1(#(ApD}z;u77|ycFHE%$!sJ<`W^x diff --git a/target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp.lock b/target/debug/incremental/chronogrid_core-25wl2zu7tc4qc/s-hl79dsuzmm-00ggpwp.lock deleted file mode 100644 index e69de29..0000000 diff --git a/target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/dep-graph.bin b/target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/dep-graph.bin deleted file mode 100644 index 3367c2bde8efc4e9d69cc6539abf33cf8270caac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12819 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 dGlwjX*JE0gnx;%2xJ}Qd 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 diff --git a/target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/query-cache.bin b/target/debug/incremental/chronogrid_crypto-080ztx429suac/s-hl79dsvfnw-1d37dg4-ep33nnmy8je5jvu0aacp9makh/query-cache.bin deleted file mode 100644 index 6140aa3de6d6cfc545f3e664dab77d03fd402a50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 815 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 H%*g)$S7bAf diff --git a/target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/metadata.rmeta b/target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/metadata.rmeta deleted file mode 100644 index 3099f4423d40b37229d9627624545c0ecdb5e381..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2006 zcmb_ddu$X%7@wKl*0vly`T}YQPAJ79Tyy)l-MvCI*lJr0PzVHQ4=($dy`Eg}&fT-6 zh6GxC=a+q)IMqQX3fFyc zYJJgD$2RuuXZqw^!}Jlbw`#igKM^tt&8(dJyUc!4fGS8 z=M}>*n3`b*C=iTKZ#itIjBrQ7T45(uorLv-MV#JqC3V8t4~lz2le(9eOo_M-B)`EC z$>2CiQUyU19RTuMRl8NQ5*=|TZv!{^C9Ut!_E-4%&0k));#wGjWcU@y&(X+&ECguC z@*9OzW3hzpT>9jr!0CB2ckkMAclqqIukCX^Xo4&XoaxsTO%HH}8?(S&u0c{nPdOEf z4mU3Ezc6jquAx=qo|t;tH3^t9CrAos^15d5l0;1khl3iVO^rul#CfckQ4U`Hs_f3L zlC{gu|F*z2<2An+G*w;Hw4li;DqX9mBN{gn9dbPlz*9Bl*e2@* z*N6lHqN#8}MHfUx_KVagS2rW!sOdbLYm8675lg z*fJCR4bmFh}r}% zc>t5RZgnP8(L|hv0PeoL-(0sZ1ic8smWfAOPACPpuUsw6iZ|d*dk5d#U31^Xdv-$R zR~AvTUcl@G3@j<#!JI({n2#9%4l>)Bqv$hqoX*n>0gwy42)IeRp|}X|B;c)t5bYb~ z_NZjz?pWgd`SRi|PtqYYn*}V(RBfbiuX%}5_xsJ!vlk~4wdCsZAoQ#w78TH3XyN&JI5 zB`FxnOQJ8S`!W}Xugz}Pozf$lyM8IEe!O4${HU{T zU==7FnG8y7dV>gJFrbm3!I?pk$i%r;>fs`de$kUI2OMzEdPX)}D`1|>vGLi75t?z! zmZe~JhGFQ5$fx6wUKfx-3_^&00k3nOMgH$PeabQ`NA9{ZU*1~_gwR}sSo&uX{F$yj R;|K!xa`%zA;WMK#@;6fW^Jf46 diff --git a/target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/query-cache.bin b/target/debug/incremental/chronogrid_net-0e97jb7s258wr/s-hl79dsvk8o-0p6zmpn-7ecux3zw5un4lsyldr24ixq8k/query-cache.bin deleted file mode 100644 index 7aba6870acc775a63f010ed1b6f2ad0affb5a1fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 815 zcmWFv_H<@okTuk^G}kj!(6C5KOfoV{Gf7i0GB7gJH89sTG|}{9U|@=tI~IM#f=jBR z-q0&3=GU=KwQYcRL;P3jgb+i6GjV{AjDv% zGm2Cq1YuS%iqtY-=w@R0#VCG@k%9RRBM`vMgqtIF4=8mTi0=dO10Z&1V1hXnK{E+6 zFu=5~jyU*fX858OCSgW|0yqmK#>l_~w+N;StiX^Fi`hWsKrM`nOgp)uvTE=CH-Gy# zf5Z3xjl2K%-}wFi03#2>&u0vVwwEV%O0iE}BouYF31%?+=6d^MyQI3e`u$J1aeuFo z-kW0YAHioT|1e+QP-&Q|`O}KkyjA|^`@oGb3GR#kcL!uD*M54w_P}&z!~3uFcHb-U z7I3oW;~+4C z?XX<>yf=;GGfaYWg3wj9e