An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: complete Phase 3 implementation with missing build and keychain files

- Add sha2, base64, uuid dependencies to Cargo.toml for DPoP proof building
- Update Cargo.lock with new dependency versions
- Add keychain module with load_dpop_key() and store_dpop_key() helpers for iOS Keychain persistence

authored by

Malpercio and committed by
Tangled
628e65f2 e6d80078

+647 -38
+509 -1
Cargo.lock
··· 161 161 ] 162 162 163 163 [[package]] 164 + name = "async-broadcast" 165 + version = "0.7.2" 166 + source = "registry+https://github.com/rust-lang/crates.io-index" 167 + checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" 168 + dependencies = [ 169 + "event-listener", 170 + "event-listener-strategy", 171 + "futures-core", 172 + "pin-project-lite", 173 + ] 174 + 175 + [[package]] 176 + name = "async-channel" 177 + version = "2.5.0" 178 + source = "registry+https://github.com/rust-lang/crates.io-index" 179 + checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" 180 + dependencies = [ 181 + "concurrent-queue", 182 + "event-listener-strategy", 183 + "futures-core", 184 + "pin-project-lite", 185 + ] 186 + 187 + [[package]] 188 + name = "async-executor" 189 + version = "1.14.0" 190 + source = "registry+https://github.com/rust-lang/crates.io-index" 191 + checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" 192 + dependencies = [ 193 + "async-task", 194 + "concurrent-queue", 195 + "fastrand", 196 + "futures-lite", 197 + "pin-project-lite", 198 + "slab", 199 + ] 200 + 201 + [[package]] 202 + name = "async-io" 203 + version = "2.6.0" 204 + source = "registry+https://github.com/rust-lang/crates.io-index" 205 + checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" 206 + dependencies = [ 207 + "autocfg", 208 + "cfg-if", 209 + "concurrent-queue", 210 + "futures-io", 211 + "futures-lite", 212 + "parking", 213 + "polling", 214 + "rustix", 215 + "slab", 216 + "windows-sys 0.61.2", 217 + ] 218 + 219 + [[package]] 220 + name = "async-lock" 221 + version = "3.4.2" 222 + source = "registry+https://github.com/rust-lang/crates.io-index" 223 + checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" 224 + dependencies = [ 225 + "event-listener", 226 + "event-listener-strategy", 227 + "pin-project-lite", 228 + ] 229 + 230 + [[package]] 231 + name = "async-process" 232 + version = "2.5.0" 233 + source = "registry+https://github.com/rust-lang/crates.io-index" 234 + checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" 235 + dependencies = [ 236 + "async-channel", 237 + "async-io", 238 + "async-lock", 239 + "async-signal", 240 + "async-task", 241 + "blocking", 242 + "cfg-if", 243 + "event-listener", 244 + "futures-lite", 245 + "rustix", 246 + ] 247 + 248 + [[package]] 249 + name = "async-recursion" 250 + version = "1.1.1" 251 + source = "registry+https://github.com/rust-lang/crates.io-index" 252 + checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" 253 + dependencies = [ 254 + "proc-macro2", 255 + "quote", 256 + "syn 2.0.117", 257 + ] 258 + 259 + [[package]] 260 + name = "async-signal" 261 + version = "0.2.13" 262 + source = "registry+https://github.com/rust-lang/crates.io-index" 263 + checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" 264 + dependencies = [ 265 + "async-io", 266 + "async-lock", 267 + "atomic-waker", 268 + "cfg-if", 269 + "futures-core", 270 + "futures-io", 271 + "rustix", 272 + "signal-hook-registry", 273 + "slab", 274 + "windows-sys 0.61.2", 275 + ] 276 + 277 + [[package]] 164 278 name = "async-stream" 165 279 version = "0.3.6" 166 280 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 181 295 "quote", 182 296 "syn 2.0.117", 183 297 ] 298 + 299 + [[package]] 300 + name = "async-task" 301 + version = "4.7.1" 302 + source = "registry+https://github.com/rust-lang/crates.io-index" 303 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 184 304 185 305 [[package]] 186 306 name = "async-trait" ··· 390 510 ] 391 511 392 512 [[package]] 513 + name = "blocking" 514 + version = "1.6.2" 515 + source = "registry+https://github.com/rust-lang/crates.io-index" 516 + checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" 517 + dependencies = [ 518 + "async-channel", 519 + "async-task", 520 + "futures-io", 521 + "futures-lite", 522 + "piper", 523 + ] 524 + 525 + [[package]] 393 526 name = "brotli" 394 527 version = "8.0.2" 395 528 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 687 820 version = "0.9.6" 688 821 source = "registry+https://github.com/rust-lang/crates.io-index" 689 822 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 823 + 824 + [[package]] 825 + name = "const-random" 826 + version = "0.1.18" 827 + source = "registry+https://github.com/rust-lang/crates.io-index" 828 + checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" 829 + dependencies = [ 830 + "const-random-macro", 831 + ] 832 + 833 + [[package]] 834 + name = "const-random-macro" 835 + version = "0.1.16" 836 + source = "registry+https://github.com/rust-lang/crates.io-index" 837 + checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" 838 + dependencies = [ 839 + "getrandom 0.2.17", 840 + "once_cell", 841 + "tiny-keccak", 842 + ] 690 843 691 844 [[package]] 692 845 name = "const-str" ··· 1151 1304 ] 1152 1305 1153 1306 [[package]] 1307 + name = "dlv-list" 1308 + version = "0.5.2" 1309 + source = "registry+https://github.com/rust-lang/crates.io-index" 1310 + checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" 1311 + dependencies = [ 1312 + "const-random", 1313 + ] 1314 + 1315 + [[package]] 1154 1316 name = "dom_query" 1155 1317 version = "0.25.1" 1156 1318 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1280 1442 ] 1281 1443 1282 1444 [[package]] 1445 + name = "endi" 1446 + version = "1.1.1" 1447 + source = "registry+https://github.com/rust-lang/crates.io-index" 1448 + checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" 1449 + 1450 + [[package]] 1283 1451 name = "enum-as-inner" 1284 1452 version = "0.6.1" 1285 1453 source = "registry+https://github.com/rust-lang/crates.io-index" 1286 1454 checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 1287 1455 dependencies = [ 1288 1456 "heck 0.5.0", 1457 + "proc-macro2", 1458 + "quote", 1459 + "syn 2.0.117", 1460 + ] 1461 + 1462 + [[package]] 1463 + name = "enumflags2" 1464 + version = "0.7.12" 1465 + source = "registry+https://github.com/rust-lang/crates.io-index" 1466 + checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" 1467 + dependencies = [ 1468 + "enumflags2_derive", 1469 + "serde", 1470 + ] 1471 + 1472 + [[package]] 1473 + name = "enumflags2_derive" 1474 + version = "0.7.12" 1475 + source = "registry+https://github.com/rust-lang/crates.io-index" 1476 + checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" 1477 + dependencies = [ 1289 1478 "proc-macro2", 1290 1479 "quote", 1291 1480 "syn 2.0.117", ··· 1341 1530 ] 1342 1531 1343 1532 [[package]] 1533 + name = "event-listener-strategy" 1534 + version = "0.5.4" 1535 + source = "registry+https://github.com/rust-lang/crates.io-index" 1536 + checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 1537 + dependencies = [ 1538 + "event-listener", 1539 + "pin-project-lite", 1540 + ] 1541 + 1542 + [[package]] 1344 1543 name = "fastrand" 1345 1544 version = "2.3.0" 1346 1545 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1541 1740 checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" 1542 1741 1543 1742 [[package]] 1743 + name = "futures-lite" 1744 + version = "2.6.1" 1745 + source = "registry+https://github.com/rust-lang/crates.io-index" 1746 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1747 + dependencies = [ 1748 + "fastrand", 1749 + "futures-core", 1750 + "futures-io", 1751 + "parking", 1752 + "pin-project-lite", 1753 + ] 1754 + 1755 + [[package]] 1544 1756 name = "futures-macro" 1545 1757 version = "0.3.32" 1546 1758 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1957 2169 1958 2170 [[package]] 1959 2171 name = "hashbrown" 2172 + version = "0.14.5" 2173 + source = "registry+https://github.com/rust-lang/crates.io-index" 2174 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 2175 + 2176 + [[package]] 2177 + name = "hashbrown" 1960 2178 version = "0.15.5" 1961 2179 source = "registry+https://github.com/rust-lang/crates.io-index" 1962 2180 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" ··· 2236 2454 "tokio", 2237 2455 "tower-service", 2238 2456 "tracing", 2239 - "windows-registry", 2457 + "windows-registry 0.6.1", 2240 2458 ] 2241 2459 2242 2460 [[package]] ··· 2370 2588 name = "identity-wallet" 2371 2589 version = "0.1.0" 2372 2590 dependencies = [ 2591 + "base64 0.21.7", 2373 2592 "crypto", 2374 2593 "multibase", 2375 2594 "p256", ··· 2377 2596 "security-framework", 2378 2597 "serde", 2379 2598 "serde_json", 2599 + "sha2", 2380 2600 "tauri", 2381 2601 "tauri-build", 2602 + "tauri-plugin-deep-link", 2603 + "tauri-plugin-opener", 2382 2604 "thiserror 2.0.18", 2383 2605 "tracing", 2606 + "url", 2607 + "uuid", 2384 2608 ] 2385 2609 2386 2610 [[package]] ··· 2471 2695 dependencies = [ 2472 2696 "memchr", 2473 2697 "serde", 2698 + ] 2699 + 2700 + [[package]] 2701 + name = "is-docker" 2702 + version = "0.2.0" 2703 + source = "registry+https://github.com/rust-lang/crates.io-index" 2704 + checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" 2705 + dependencies = [ 2706 + "once_cell", 2707 + ] 2708 + 2709 + [[package]] 2710 + name = "is-wsl" 2711 + version = "0.4.0" 2712 + source = "registry+https://github.com/rust-lang/crates.io-index" 2713 + checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" 2714 + dependencies = [ 2715 + "is-docker", 2716 + "once_cell", 2474 2717 ] 2475 2718 2476 2719 [[package]] ··· 3210 3453 checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 3211 3454 3212 3455 [[package]] 3456 + name = "open" 3457 + version = "5.3.3" 3458 + source = "registry+https://github.com/rust-lang/crates.io-index" 3459 + checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" 3460 + dependencies = [ 3461 + "dunce", 3462 + "is-wsl", 3463 + "libc", 3464 + "pathdiff", 3465 + ] 3466 + 3467 + [[package]] 3213 3468 name = "openssl" 3214 3469 version = "0.10.76" 3215 3470 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3342 3597 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 3343 3598 3344 3599 [[package]] 3600 + name = "ordered-multimap" 3601 + version = "0.7.3" 3602 + source = "registry+https://github.com/rust-lang/crates.io-index" 3603 + checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" 3604 + dependencies = [ 3605 + "dlv-list", 3606 + "hashbrown 0.14.5", 3607 + ] 3608 + 3609 + [[package]] 3610 + name = "ordered-stream" 3611 + version = "0.2.0" 3612 + source = "registry+https://github.com/rust-lang/crates.io-index" 3613 + checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 3614 + dependencies = [ 3615 + "futures-core", 3616 + "pin-project-lite", 3617 + ] 3618 + 3619 + [[package]] 3345 3620 name = "p256" 3346 3621 version = "0.13.2" 3347 3622 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3417 3692 "rand_core 0.6.4", 3418 3693 "subtle", 3419 3694 ] 3695 + 3696 + [[package]] 3697 + name = "pathdiff" 3698 + version = "0.2.3" 3699 + source = "registry+https://github.com/rust-lang/crates.io-index" 3700 + checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" 3420 3701 3421 3702 [[package]] 3422 3703 name = "pem" ··· 3663 3944 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 3664 3945 3665 3946 [[package]] 3947 + name = "piper" 3948 + version = "0.2.5" 3949 + source = "registry+https://github.com/rust-lang/crates.io-index" 3950 + checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" 3951 + dependencies = [ 3952 + "atomic-waker", 3953 + "fastrand", 3954 + "futures-io", 3955 + ] 3956 + 3957 + [[package]] 3666 3958 name = "pkcs1" 3667 3959 version = "0.7.5" 3668 3960 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3719 4011 "fdeflate", 3720 4012 "flate2", 3721 4013 "miniz_oxide", 4014 + ] 4015 + 4016 + [[package]] 4017 + name = "polling" 4018 + version = "3.11.0" 4019 + source = "registry+https://github.com/rust-lang/crates.io-index" 4020 + checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" 4021 + dependencies = [ 4022 + "cfg-if", 4023 + "concurrent-queue", 4024 + "hermit-abi", 4025 + "pin-project-lite", 4026 + "rustix", 4027 + "windows-sys 0.61.2", 3722 4028 ] 3723 4029 3724 4030 [[package]] ··· 4327 4633 "spki", 4328 4634 "subtle", 4329 4635 "zeroize", 4636 + ] 4637 + 4638 + [[package]] 4639 + name = "rust-ini" 4640 + version = "0.21.3" 4641 + source = "registry+https://github.com/rust-lang/crates.io-index" 4642 + checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" 4643 + dependencies = [ 4644 + "cfg-if", 4645 + "ordered-multimap", 4330 4646 ] 4331 4647 4332 4648 [[package]] ··· 5469 5785 ] 5470 5786 5471 5787 [[package]] 5788 + name = "tauri-plugin" 5789 + version = "2.5.4" 5790 + source = "registry+https://github.com/rust-lang/crates.io-index" 5791 + checksum = "ddde7d51c907b940fb573006cdda9a642d6a7c8153657e88f8a5c3c9290cd4aa" 5792 + dependencies = [ 5793 + "anyhow", 5794 + "glob", 5795 + "plist", 5796 + "schemars 0.8.22", 5797 + "serde", 5798 + "serde_json", 5799 + "tauri-utils", 5800 + "toml 0.9.12+spec-1.1.0", 5801 + "walkdir", 5802 + ] 5803 + 5804 + [[package]] 5805 + name = "tauri-plugin-deep-link" 5806 + version = "2.4.7" 5807 + source = "registry+https://github.com/rust-lang/crates.io-index" 5808 + checksum = "94deb2e2e4641514ac496db2cddcfc850d6fc9d51ea17b82292a0490bd20ba5b" 5809 + dependencies = [ 5810 + "dunce", 5811 + "plist", 5812 + "rust-ini", 5813 + "serde", 5814 + "serde_json", 5815 + "tauri", 5816 + "tauri-plugin", 5817 + "tauri-utils", 5818 + "thiserror 2.0.18", 5819 + "tracing", 5820 + "url", 5821 + "windows-registry 0.5.3", 5822 + "windows-result 0.3.4", 5823 + ] 5824 + 5825 + [[package]] 5826 + name = "tauri-plugin-opener" 5827 + version = "2.5.3" 5828 + source = "registry+https://github.com/rust-lang/crates.io-index" 5829 + checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" 5830 + dependencies = [ 5831 + "dunce", 5832 + "glob", 5833 + "objc2-app-kit", 5834 + "objc2-foundation", 5835 + "open", 5836 + "schemars 0.8.22", 5837 + "serde", 5838 + "serde_json", 5839 + "tauri", 5840 + "tauri-plugin", 5841 + "thiserror 2.0.18", 5842 + "url", 5843 + "windows", 5844 + "zbus", 5845 + ] 5846 + 5847 + [[package]] 5472 5848 name = "tauri-runtime" 5473 5849 version = "2.10.1" 5474 5850 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5673 6049 ] 5674 6050 5675 6051 [[package]] 6052 + name = "tiny-keccak" 6053 + version = "2.0.2" 6054 + source = "registry+https://github.com/rust-lang/crates.io-index" 6055 + checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 6056 + dependencies = [ 6057 + "crunchy", 6058 + ] 6059 + 6060 + [[package]] 5676 6061 name = "tinystr" 5677 6062 version = "0.8.2" 5678 6063 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6124 6509 version = "1.19.0" 6125 6510 source = "registry+https://github.com/rust-lang/crates.io-index" 6126 6511 checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 6512 + 6513 + [[package]] 6514 + name = "uds_windows" 6515 + version = "1.2.1" 6516 + source = "registry+https://github.com/rust-lang/crates.io-index" 6517 + checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" 6518 + dependencies = [ 6519 + "memoffset", 6520 + "tempfile", 6521 + "windows-sys 0.61.2", 6522 + ] 6127 6523 6128 6524 [[package]] 6129 6525 name = "unic-char-property" ··· 6769 7165 6770 7166 [[package]] 6771 7167 name = "windows-registry" 7168 + version = "0.5.3" 7169 + source = "registry+https://github.com/rust-lang/crates.io-index" 7170 + checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 7171 + dependencies = [ 7172 + "windows-link 0.1.3", 7173 + "windows-result 0.3.4", 7174 + "windows-strings 0.4.2", 7175 + ] 7176 + 7177 + [[package]] 7178 + name = "windows-registry" 6772 7179 version = "0.6.1" 6773 7180 source = "registry+https://github.com/rust-lang/crates.io-index" 6774 7181 checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" ··· 7373 7780 ] 7374 7781 7375 7782 [[package]] 7783 + name = "zbus" 7784 + version = "5.14.0" 7785 + source = "registry+https://github.com/rust-lang/crates.io-index" 7786 + checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" 7787 + dependencies = [ 7788 + "async-broadcast", 7789 + "async-executor", 7790 + "async-io", 7791 + "async-lock", 7792 + "async-process", 7793 + "async-recursion", 7794 + "async-task", 7795 + "async-trait", 7796 + "blocking", 7797 + "enumflags2", 7798 + "event-listener", 7799 + "futures-core", 7800 + "futures-lite", 7801 + "hex", 7802 + "libc", 7803 + "ordered-stream", 7804 + "rustix", 7805 + "serde", 7806 + "serde_repr", 7807 + "tracing", 7808 + "uds_windows", 7809 + "uuid", 7810 + "windows-sys 0.61.2", 7811 + "winnow 0.7.15", 7812 + "zbus_macros", 7813 + "zbus_names", 7814 + "zvariant", 7815 + ] 7816 + 7817 + [[package]] 7818 + name = "zbus_macros" 7819 + version = "5.14.0" 7820 + source = "registry+https://github.com/rust-lang/crates.io-index" 7821 + checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" 7822 + dependencies = [ 7823 + "proc-macro-crate 3.5.0", 7824 + "proc-macro2", 7825 + "quote", 7826 + "syn 2.0.117", 7827 + "zbus_names", 7828 + "zvariant", 7829 + "zvariant_utils", 7830 + ] 7831 + 7832 + [[package]] 7833 + name = "zbus_names" 7834 + version = "4.3.1" 7835 + source = "registry+https://github.com/rust-lang/crates.io-index" 7836 + checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" 7837 + dependencies = [ 7838 + "serde", 7839 + "winnow 0.7.15", 7840 + "zvariant", 7841 + ] 7842 + 7843 + [[package]] 7376 7844 name = "zerocopy" 7377 7845 version = "0.8.42" 7378 7846 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 7457 7925 version = "1.0.21" 7458 7926 source = "registry+https://github.com/rust-lang/crates.io-index" 7459 7927 checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" 7928 + 7929 + [[package]] 7930 + name = "zvariant" 7931 + version = "5.10.0" 7932 + source = "registry+https://github.com/rust-lang/crates.io-index" 7933 + checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" 7934 + dependencies = [ 7935 + "endi", 7936 + "enumflags2", 7937 + "serde", 7938 + "winnow 0.7.15", 7939 + "zvariant_derive", 7940 + "zvariant_utils", 7941 + ] 7942 + 7943 + [[package]] 7944 + name = "zvariant_derive" 7945 + version = "5.10.0" 7946 + source = "registry+https://github.com/rust-lang/crates.io-index" 7947 + checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" 7948 + dependencies = [ 7949 + "proc-macro-crate 3.5.0", 7950 + "proc-macro2", 7951 + "quote", 7952 + "syn 2.0.117", 7953 + "zvariant_utils", 7954 + ] 7955 + 7956 + [[package]] 7957 + name = "zvariant_utils" 7958 + version = "3.3.0" 7959 + source = "registry+https://github.com/rust-lang/crates.io-index" 7960 + checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" 7961 + dependencies = [ 7962 + "proc-macro2", 7963 + "quote", 7964 + "serde", 7965 + "syn 2.0.117", 7966 + "winnow 0.7.15", 7967 + ]
+3
apps/identity-wallet/src-tauri/Cargo.toml
··· 30 30 p256 = { workspace = true } 31 31 multibase = { workspace = true } 32 32 tracing = { workspace = true } 33 + sha2 = { workspace = true } 34 + base64 = { workspace = true } 35 + uuid = { workspace = true } 33 36 34 37 [build-dependencies] 35 38 # Tauri-specific — declared locally
+63
apps/identity-wallet/src-tauri/src/keychain.rs
··· 81 81 } 82 82 } 83 83 84 + // ── OAuth Keychain helpers ───────────────────────────────────────────────────── 85 + 86 + const DPOP_KEY_PRIV_ACCOUNT: &str = "oauth-dpop-key-priv"; 87 + const OAUTH_ACCESS_TOKEN_ACCOUNT: &str = "oauth-access-token"; 88 + const OAUTH_REFRESH_TOKEN_ACCOUNT: &str = "oauth-refresh-token"; 89 + 90 + /// Store the DPoP private key scalar (32 bytes) in the Keychain. 91 + pub fn store_dpop_key(private_bytes: &[u8]) -> Result<(), KeychainError> { 92 + store_item(DPOP_KEY_PRIV_ACCOUNT, private_bytes) 93 + } 94 + 95 + /// Load the DPoP private key scalar from the Keychain. 96 + /// 97 + /// Returns `None` if no key has been stored yet (first run). 98 + pub fn load_dpop_key() -> Option<[u8; 32]> { 99 + match get_item(DPOP_KEY_PRIV_ACCOUNT) { 100 + Ok(bytes) if bytes.len() == 32 => { 101 + let mut arr = [0u8; 32]; 102 + arr.copy_from_slice(&bytes); 103 + Some(arr) 104 + } 105 + Ok(_) => { 106 + tracing::warn!("DPoP key in Keychain has unexpected length; treating as absent"); 107 + None 108 + } 109 + Err(e) if is_not_found(&e) => None, 110 + Err(e) => { 111 + tracing::error!(error = ?e, "Keychain error loading DPoP key"); 112 + None 113 + } 114 + } 115 + } 116 + 117 + /// Store the OAuth access token and refresh token in the Keychain. 118 + pub fn store_oauth_tokens(access_token: &str, refresh_token: &str) -> Result<(), KeychainError> { 119 + store_item(OAUTH_ACCESS_TOKEN_ACCOUNT, access_token.as_bytes())?; 120 + store_item(OAUTH_REFRESH_TOKEN_ACCOUNT, refresh_token.as_bytes())?; 121 + Ok(()) 122 + } 123 + 124 + /// Load the OAuth access token and refresh token from the Keychain. 125 + /// 126 + /// Returns `None` if either token is missing (not yet authenticated). 127 + pub fn load_oauth_tokens() -> Option<(String, String)> { 128 + let access = match get_item(OAUTH_ACCESS_TOKEN_ACCOUNT) { 129 + Ok(b) => String::from_utf8(b).ok()?, 130 + Err(e) if is_not_found(&e) => return None, 131 + Err(e) => { 132 + tracing::error!(error = ?e, "Keychain error loading access token"); 133 + return None; 134 + } 135 + }; 136 + let refresh = match get_item(OAUTH_REFRESH_TOKEN_ACCOUNT) { 137 + Ok(b) => String::from_utf8(b).ok()?, 138 + Err(e) if is_not_found(&e) => return None, 139 + Err(e) => { 140 + tracing::error!(error = ?e, "Keychain error loading refresh token"); 141 + return None; 142 + } 143 + }; 144 + Some((access, refresh)) 145 + } 146 + 84 147 /// In-memory Keychain substitute used exclusively in test builds. 85 148 #[cfg(test)] 86 149 mod test_store {
+72 -37
apps/identity-wallet/src-tauri/src/oauth.rs
··· 4 4 // handle_deep_link: Imperative Shell (reads OS callback, routes to pending channel) 5 5 6 6 use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; 7 - use p256::ecdsa::{SigningKey, Signature, signature::Signer}; 7 + use p256::ecdsa::{signature::Signer, Signature, SigningKey}; 8 8 #[allow(unused_imports)] 9 9 use p256::elliptic_curve::sec1::ToEncodedPoint; 10 10 use sha2::{Digest, Sha256}; ··· 89 89 /// Load the DPoP keypair from Keychain, or generate and persist a new one. 90 90 pub fn get_or_create() -> Result<Self, OAuthError> { 91 91 if let Some(private_bytes) = crate::keychain::load_dpop_key() { 92 - let signing_key = SigningKey::from_slice(&private_bytes) 93 - .map_err(|_| OAuthError::DpopKeyInvalid)?; 92 + let signing_key = 93 + SigningKey::from_slice(&private_bytes).map_err(|_| OAuthError::DpopKeyInvalid)?; 94 94 return Ok(Self { signing_key }); 95 95 } 96 96 ··· 99 99 // `private_key_bytes` is `Zeroizing<[u8; 32]>`, which derefs directly to `[u8; 32]`. 100 100 let private_bytes: [u8; 32] = *keypair.private_key_bytes; 101 101 102 - crate::keychain::store_dpop_key(&private_bytes) 103 - .map_err(|_| OAuthError::KeychainError)?; 102 + crate::keychain::store_dpop_key(&private_bytes).map_err(|_| OAuthError::KeychainError)?; 104 103 105 - let signing_key = SigningKey::from_slice(&private_bytes) 106 - .map_err(|_| OAuthError::DpopKeyInvalid)?; 104 + let signing_key = 105 + SigningKey::from_slice(&private_bytes).map_err(|_| OAuthError::DpopKeyInvalid)?; 107 106 Ok(Self { signing_key }) 108 107 } 109 108 ··· 169 168 "alg": "ES256", 170 169 "jwk": jwk, 171 170 }); 172 - let header_b64 = URL_SAFE_NO_PAD.encode( 173 - serde_json::to_vec(&header).map_err(|_| OAuthError::DpopProofFailed)?, 174 - ); 171 + let header_b64 = URL_SAFE_NO_PAD 172 + .encode(serde_json::to_vec(&header).map_err(|_| OAuthError::DpopProofFailed)?); 175 173 176 174 // Claims JSON. 177 175 let iat = SystemTime::now() ··· 193 191 claims["ath"] = serde_json::Value::String(a.to_string()); 194 192 } 195 193 196 - let claims_b64 = URL_SAFE_NO_PAD.encode( 197 - serde_json::to_vec(&claims).map_err(|_| OAuthError::DpopProofFailed)?, 198 - ); 194 + let claims_b64 = URL_SAFE_NO_PAD 195 + .encode(serde_json::to_vec(&claims).map_err(|_| OAuthError::DpopProofFailed)?); 199 196 200 197 // Sign `header_b64.claims_b64` bytes with P-256/SHA-256. 201 198 let signing_input = format!("{header_b64}.{claims_b64}"); ··· 293 290 294 291 #[test] 295 292 fn dpop_proof_header_has_required_fields() { 296 - // MM-149.AC3.1 297 293 let kp = DPoPKeypair::get_or_create().expect("keypair must generate"); 298 - let proof = kp.make_proof("POST", "https://example.com/oauth/token", None, None) 294 + let proof = kp 295 + .make_proof("POST", "https://example.com/oauth/token", None, None) 299 296 .expect("proof must build"); 300 297 let (header_b64, _, _) = split_proof(&proof); 301 298 let header = decode_jwt_part(header_b64); ··· 304 301 assert_eq!(header["alg"].as_str(), Some("ES256")); 305 302 assert_eq!(header["jwk"]["kty"].as_str(), Some("EC")); 306 303 assert_eq!(header["jwk"]["crv"].as_str(), Some("P-256")); 307 - assert!(header["jwk"]["x"].as_str().map(|s| !s.is_empty()).unwrap_or(false)); 308 - assert!(header["jwk"]["y"].as_str().map(|s| !s.is_empty()).unwrap_or(false)); 304 + assert!(header["jwk"]["x"] 305 + .as_str() 306 + .map(|s| !s.is_empty()) 307 + .unwrap_or(false)); 308 + assert!(header["jwk"]["y"] 309 + .as_str() 310 + .map(|s| !s.is_empty()) 311 + .unwrap_or(false)); 309 312 } 310 313 311 314 #[test] 312 315 fn dpop_proof_claims_has_required_fields() { 313 - // MM-149.AC3.2 314 316 let kp = DPoPKeypair::get_or_create().expect("keypair must generate"); 315 - let proof = kp.make_proof("GET", "https://example.com/xrpc/foo", None, None) 317 + let proof = kp 318 + .make_proof("GET", "https://example.com/xrpc/foo", None, None) 316 319 .expect("proof must build"); 317 320 let (_, claims_b64, _) = split_proof(&proof); 318 321 let claims = decode_jwt_part(claims_b64); 319 322 320 - assert!(claims["jti"].as_str().map(|s| !s.is_empty()).unwrap_or(false)); 323 + assert!(claims["jti"] 324 + .as_str() 325 + .map(|s| !s.is_empty()) 326 + .unwrap_or(false)); 321 327 assert_eq!(claims["htm"].as_str(), Some("GET")); 322 328 assert_eq!(claims["htu"].as_str(), Some("https://example.com/xrpc/foo")); 323 329 let now = std::time::SystemTime::now() ··· 330 336 331 337 #[test] 332 338 fn dpop_proof_includes_ath_when_supplied() { 333 - // MM-149.AC3.3 334 339 let kp = DPoPKeypair::get_or_create().expect("keypair must generate"); 335 - let proof_with = kp.make_proof("GET", "https://example.com/resource", None, Some("abc123")) 340 + let proof_with = kp 341 + .make_proof("GET", "https://example.com/resource", None, Some("abc123")) 336 342 .expect("proof with ath must build"); 337 343 let (_, claims_b64, _) = split_proof(&proof_with); 338 344 let claims = decode_jwt_part(claims_b64); 339 - assert_eq!(claims["ath"].as_str(), Some("abc123"), "ath must be present"); 345 + assert_eq!( 346 + claims["ath"].as_str(), 347 + Some("abc123"), 348 + "ath must be present" 349 + ); 340 350 341 - let proof_without = kp.make_proof("GET", "https://example.com/resource", None, None) 351 + let proof_without = kp 352 + .make_proof("GET", "https://example.com/resource", None, None) 342 353 .expect("proof without ath must build"); 343 354 let (_, claims_b64, _) = split_proof(&proof_without); 344 355 let claims = decode_jwt_part(claims_b64); 345 - assert!(claims["ath"].is_null(), "ath must be absent when not supplied"); 356 + assert!( 357 + claims["ath"].is_null(), 358 + "ath must be absent when not supplied" 359 + ); 346 360 } 347 361 348 362 #[test] 349 363 fn dpop_proof_includes_nonce_when_supplied() { 350 - // MM-149.AC3.4 351 364 let kp = DPoPKeypair::get_or_create().expect("keypair must generate"); 352 - let proof = kp.make_proof("POST", "https://example.com/oauth/token", Some("nonce123"), None) 365 + let proof = kp 366 + .make_proof( 367 + "POST", 368 + "https://example.com/oauth/token", 369 + Some("nonce123"), 370 + None, 371 + ) 353 372 .expect("proof with nonce must build"); 354 373 let (_, claims_b64, _) = split_proof(&proof); 355 374 let claims = decode_jwt_part(claims_b64); 356 - assert_eq!(claims["nonce"].as_str(), Some("nonce123"), "nonce must be present"); 375 + assert_eq!( 376 + claims["nonce"].as_str(), 377 + Some("nonce123"), 378 + "nonce must be present" 379 + ); 357 380 358 - let proof_no = kp.make_proof("POST", "https://example.com/oauth/token", None, None) 381 + let proof_no = kp 382 + .make_proof("POST", "https://example.com/oauth/token", None, None) 359 383 .expect("proof without nonce must build"); 360 384 let (_, claims_b64, _) = split_proof(&proof_no); 361 385 let claims = decode_jwt_part(claims_b64); 362 - assert!(claims["nonce"].is_null(), "nonce must be absent when not supplied"); 386 + assert!( 387 + claims["nonce"].is_null(), 388 + "nonce must be absent when not supplied" 389 + ); 363 390 } 364 391 365 392 #[test] 366 393 fn dpop_proof_signature_verifies_against_embedded_jwk() { 367 - // MM-149.AC3.5 368 394 use p256::elliptic_curve::sec1::EncodedPoint; 369 395 370 396 let kp = DPoPKeypair::get_or_create().expect("keypair must generate"); 371 - let proof = kp.make_proof("POST", "https://example.com/oauth/token", None, None) 397 + let proof = kp 398 + .make_proof("POST", "https://example.com/oauth/token", None, None) 372 399 .expect("proof must build"); 373 400 let (header_b64, claims_b64, sig_b64) = split_proof(&proof); 374 401 375 402 // Reconstruct verifying key from the embedded JWK. 376 403 let header = decode_jwt_part(header_b64); 377 - let x_bytes = URL_SAFE_NO_PAD.decode(header["jwk"]["x"].as_str().unwrap()).unwrap(); 378 - let y_bytes = URL_SAFE_NO_PAD.decode(header["jwk"]["y"].as_str().unwrap()).unwrap(); 404 + let x_bytes = URL_SAFE_NO_PAD 405 + .decode(header["jwk"]["x"].as_str().unwrap()) 406 + .unwrap(); 407 + let y_bytes = URL_SAFE_NO_PAD 408 + .decode(header["jwk"]["y"].as_str().unwrap()) 409 + .unwrap(); 379 410 // Build uncompressed point: 0x04 || x || y 380 411 let mut point_bytes = vec![0x04u8]; 381 412 point_bytes.extend_from_slice(&x_bytes); 382 413 point_bytes.extend_from_slice(&y_bytes); 383 - let point = EncodedPoint::<p256::NistP256>::from_bytes(&point_bytes).expect("valid uncompressed point"); 414 + let point = EncodedPoint::<p256::NistP256>::from_bytes(&point_bytes) 415 + .expect("valid uncompressed point"); 384 416 let verifying_key = p256::ecdsa::VerifyingKey::from_encoded_point(&point) 385 417 .expect("valid verifying key from JWK"); 386 418 387 419 // Decode the signature. 388 - let sig_bytes = URL_SAFE_NO_PAD.decode(sig_b64).expect("valid base64url sig"); 420 + let sig_bytes = URL_SAFE_NO_PAD 421 + .decode(sig_b64) 422 + .expect("valid base64url sig"); 389 423 let signature = p256::ecdsa::Signature::from_bytes(sig_bytes.as_slice().into()) 390 424 .expect("valid R||S signature bytes"); 391 425 392 426 // Verify the signature over the signing input. 393 427 let signing_input = format!("{header_b64}.{claims_b64}"); 394 - verifying_key.verify(signing_input.as_bytes(), &signature) 428 + verifying_key 429 + .verify(signing_input.as_bytes(), &signature) 395 430 .expect("signature must verify against embedded JWK"); 396 431 } 397 432