diff --git a/Cargo.lock b/Cargo.lock index 90e3097a..a83e4c85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aead" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99" +dependencies = [ + "crypto-common 0.2.2", + "inout 0.2.2", +] + [[package]] name = "aes" version = "0.8.4" @@ -61,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -71,9 +81,9 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead", + "aead 0.5.2", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -1781,6 +1791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", + "zeroize", ] [[package]] @@ -1993,7 +2004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] @@ -2004,6 +2015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", + "cipher 0.5.2", "cpufeatures 0.3.0", "rand_core 0.10.1", ] @@ -2014,13 +2026,25 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", + "aead 0.5.2", "chacha20 0.9.1", - "cipher", - "poly1305", + "cipher 0.4.4", + "poly1305 0.8.0", "zeroize", ] +[[package]] +name = "chacha20poly1305" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb" +dependencies = [ + "aead 0.6.1", + "chacha20 0.10.1", + "cipher 0.5.2", + "poly1305 0.9.1", +] + [[package]] name = "chrono" version = "0.4.45" @@ -2069,10 +2093,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", - "inout", + "inout 0.1.4", "zeroize", ] +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "inout 0.2.2", +] + [[package]] name = "circom-witness-rs" version = "0.2.3" @@ -2144,6 +2179,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "cobs" version = "0.3.0" @@ -2224,7 +2265,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2253,6 +2294,12 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" +[[package]] +name = "const-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + [[package]] name = "const-hex" version = "1.19.1" @@ -2313,6 +2360,35 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -2448,6 +2524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", + "rand_core 0.10.1", ] [[package]] @@ -2487,7 +2564,16 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", ] [[package]] @@ -2499,9 +2585,24 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "fiat-crypto", + "fiat-crypto 0.2.9", + "rustc_version 0.4.1", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "curve25519-dalek-derive", + "fiat-crypto 0.3.0", "rustc_version 0.4.1", "subtle", + "zeroize", ] [[package]] @@ -2606,10 +2707,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid 0.9.6", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "deranged" version = "0.5.8" @@ -2705,6 +2819,8 @@ dependencies = [ "block-buffer 0.12.1", "const-oid 0.10.2", "crypto-common 0.2.2", + "ctutils", + "zeroize", ] [[package]] @@ -2735,7 +2851,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2760,6 +2876,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -2910,7 +3035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2979,6 +3104,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "filetime" version = "0.2.29" @@ -3013,6 +3144,64 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + +[[package]] +name = "flamingo-verifier-api-types" +version = "0.2.0" +source = "git+https://github.com/worldcoin/flamingo?rev=f0f1814e58c83f772a6fc8f8e76476e1060f8f64#f0f1814e58c83f772a6fc8f8e76476e1060f8f64" +dependencies = [ + "serde", +] + +[[package]] +name = "flamingo-verifier-client" +version = "0.2.0" +source = "git+https://github.com/worldcoin/flamingo?rev=f0f1814e58c83f772a6fc8f8e76476e1060f8f64#f0f1814e58c83f772a6fc8f8e76476e1060f8f64" +dependencies = [ + "base64 0.22.1", + "flamingo-verifier-api-types", + "flamingo-verifier-protocol", + "flamingo-verifier-sealed-types", + "hex", + "pontifex", + "reqwest 0.12.28", + "serde", + "serde_json", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "flamingo-verifier-protocol" +version = "0.2.0" +source = "git+https://github.com/worldcoin/flamingo?rev=f0f1814e58c83f772a6fc8f8e76476e1060f8f64#f0f1814e58c83f772a6fc8f8e76476e1060f8f64" +dependencies = [ + "ark-ff 0.5.0", + "coset", + "serde", + "serde_bytes", + "taceo-ark-babyjubjub", + "taceo-eddsa-babyjubjub", + "taceo-poseidon2", +] + +[[package]] +name = "flamingo-verifier-sealed-types" +version = "0.2.0" +source = "git+https://github.com/worldcoin/flamingo?rev=f0f1814e58c83f772a6fc8f8e76476e1060f8f64#f0f1814e58c83f772a6fc8f8e76476e1060f8f64" +dependencies = [ + "ciborium", + "flamingo-verifier-protocol", + "serde", + "serde_bytes", + "zeroize", +] + [[package]] name = "flate2" version = "1.1.9" @@ -3432,6 +3621,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-conservative" @@ -3463,7 +3655,16 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", ] [[package]] @@ -3475,23 +3676,54 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "hpke" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f65d16b699dd1a1fa2d851c970b0c971b388eeeb40f744252b8de48860980c8f" dependencies = [ - "aead", + "aead 0.5.2", "aes-gcm", - "chacha20poly1305", + "chacha20poly1305 0.10.1", "digest 0.10.7", "generic-array", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", "rand_core 0.9.5", "sha2 0.10.9", "subtle", - "x25519-dalek", + "x25519-dalek 2.0.1", + "zeroize", +] + +[[package]] +name = "hpke" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5130e119706b4d8c2180da6126f7e60b6c38c2d340d539219f57051f0a7af7" +dependencies = [ + "aead 0.6.1", + "chacha20poly1305 0.11.0", + "hkdf 0.13.0", + "hybrid-array", + "ml-kem", + "rand_core 0.10.1", + "sha2 0.11.0", + "sha3 0.12.0", + "shake", + "subtle", + "turboshake", + "x-wing", + "x25519-dalek 3.0.0", "zeroize", ] @@ -3546,7 +3778,9 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ + "ctutils", "typenum", + "zeroize", ] [[package]] @@ -3862,6 +4096,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "inplace-vec-builder" version = "0.1.1" @@ -3885,7 +4128,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4158,6 +4401,16 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "kem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd" +dependencies = [ + "crypto-common 0.2.2", + "rand_core 0.10.1", +] + [[package]] name = "konst" version = "0.2.20" @@ -4218,6 +4471,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -4473,6 +4732,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ml-kem" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e15f3e5b957493873e396a66914e83e616b6afe335cdef7efe5c6e1216aba66" +dependencies = [ + "hybrid-array", + "kem", + "module-lattice", + "rand_core 0.10.1", + "sha3 0.11.0", + "zeroize", +] + [[package]] name = "mmap-rs" version = "0.6.1" @@ -4515,6 +4788,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "module-lattice" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", + "zeroize", +] + [[package]] name = "multimap" version = "0.10.1" @@ -4550,7 +4835,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4650,13 +4935,13 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41a03aaaf57495c75ce66aee6a7c3b21abf046c9d4cca3d45b22cdbf0de1bfba" dependencies = [ - "aead", + "aead 0.5.2", "aes-gcm", "byteorder", - "chacha20poly1305", + "chacha20poly1305 0.10.1", "hex", - "hkdf", - "hpke", + "hkdf 0.12.4", + "hpke 0.13.0", "log", "rand 0.9.4", "serde", @@ -4881,6 +5166,18 @@ dependencies = [ "transpose", ] +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -5065,7 +5362,17 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" +dependencies = [ + "cpufeatures 0.3.0", + "universal-hash 0.6.1", ] [[package]] @@ -5077,7 +5384,29 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "opaque-debug", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "pontifex" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f05f626f4b534d7891a41b0a0ffc47e8e7eafa40c63f4dbacfa9b2828527195" +dependencies = [ + "ciborium", + "const-fnv1a-hash", + "coset", + "p384", + "quantum-box", + "serde", + "serde_bytes", + "sha2 0.10.9", + "thiserror 2.0.18", + "tokio", + "tracing", + "webpki", + "x509-cert", + "zeroize", ] [[package]] @@ -5854,6 +6183,36 @@ dependencies = [ "serde_json", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "quantum-box" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a8fbba8d8939e7e5f503520a27bae93371df6c6ffe8de7f804de59bb3d29c0" +dependencies = [ + "base64 0.22.1", + "getrandom 0.4.3", + "hpke 0.14.0", + "thiserror 2.0.18", + "x-wing", + "zeroize", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -5914,7 +6273,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6183,6 +6542,8 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", + "cookie", + "cookie_store", "futures-channel", "futures-core", "futures-util", @@ -6205,12 +6566,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 1.0.8", ] @@ -6258,7 +6621,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -6442,7 +6805,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6522,7 +6885,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7073,6 +7436,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.229" @@ -7253,6 +7626,17 @@ dependencies = [ "keccak 0.2.0", ] +[[package]] +name = "sha3" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", + "sponge-cursor", +] + [[package]] name = "sha3-asm" version = "0.1.8" @@ -7263,6 +7647,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "shake" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09057cb2149ad4cbd2da1e26b351f9a4c354219421229c69c3063e6f61947c4a" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", + "sponge-cursor", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -7416,6 +7811,15 @@ dependencies = [ "der", ] +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" +dependencies = [ + "zeroize", +] + [[package]] name = "sqlite-wasm-rs" version = "0.5.5" @@ -7831,10 +8235,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8021,6 +8425,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "tokio" version = "1.52.3" @@ -8382,6 +8807,17 @@ dependencies = [ "utf-8", ] +[[package]] +name = "turboshake" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f892c6904b0bd5a9241eac848347abcbbbd2b9f3892bad23ac3e6efab8d6f06a" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", + "sponge-cursor", +] + [[package]] name = "typenum" version = "1.20.1" @@ -8587,6 +9023,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" +dependencies = [ + "crypto-common 0.2.2", + "ctutils", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -8732,17 +9178,21 @@ version = "0.21.4" dependencies = [ "alloy", "alloy-core", + "async-trait", "backon", "base64 0.22.1", - "chacha20poly1305", + "chacha20poly1305 0.10.1", "chrono", "ciborium", "ctor", "dotenvy", "eyre", + "flamingo-verifier-client", + "flamingo-verifier-protocol", + "flamingo-verifier-sealed-types", "getrandom 0.3.4", "hex", - "hkdf", + "hkdf 0.12.4", "log", "mockito", "rand 0.8.6", @@ -8946,6 +9396,19 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31d56021e873866c968588ed85ccdf56db5c426e44afdb4618c39895104b920" +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -8980,6 +9443,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki-root-certs" version = "0.26.11" @@ -9053,7 +9526,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9521,16 +9994,53 @@ dependencies = [ "tap", ] +[[package]] +name = "x-wing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51507b887016c3925c84591108dadb8c099f776eb04fec6a60ae3519fee856f" +dependencies = [ + "kem", + "ml-kem", + "sha3 0.12.0", + "shake", + "x25519-dalek 3.0.0", + "zeroize", +] + [[package]] name = "x25519-dalek" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", ] +[[package]] +name = "x25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6" +dependencies = [ + "curve25519-dalek 5.0.0", + "rand_core 0.10.1", + "zeroize", +] + +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid 0.9.6", + "der", + "spki", + "tls_codec", +] + [[package]] name = "xattr" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 3596fc34..bdc581ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ alloy = { version = "2", default-features = false } alloy-core = { version = "1", default-features = false, features = [ "sol-types", ] } +async-trait = "0.1" backon = "1.6" base64 = "0.22" cc = "1" @@ -40,6 +41,10 @@ clap = "4" ctor = "0.2" dirs = "6" dotenvy = "0.15.7" +# TODO: Replace these pins with a Flamingo release containing PR #97. +flamingo-verifier-client = { git = "https://github.com/worldcoin/flamingo", rev = "f0f1814e58c83f772a6fc8f8e76476e1060f8f64" } +flamingo-verifier-protocol = { git = "https://github.com/worldcoin/flamingo", rev = "f0f1814e58c83f772a6fc8f8e76476e1060f8f64" } +flamingo-verifier-sealed-types = { git = "https://github.com/worldcoin/flamingo", rev = "f0f1814e58c83f772a6fc8f8e76476e1060f8f64" } eyre = "0.6" getrandom = "0.3" hex = "0.4" diff --git a/crates/walletkit-core/Cargo.toml b/crates/walletkit-core/Cargo.toml index fb1919e1..07483042 100644 --- a/crates/walletkit-core/Cargo.toml +++ b/crates/walletkit-core/Cargo.toml @@ -57,7 +57,11 @@ getrandom = { workspace = true, features = ["wasm_js"] } # Native-only dependencies (not available on wasm32) [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +async-trait = { workspace = true } ctor = { workspace = true } +flamingo-verifier-client = { workspace = true } +flamingo-verifier-protocol = { workspace = true } +flamingo-verifier-sealed-types = { workspace = true } reqwest = { workspace = true, features = ["brotli", "rustls-tls"] } rustls = { workspace = true, features = ["ring"] } diff --git a/crates/walletkit-core/src/flamingo.rs b/crates/walletkit-core/src/flamingo.rs new file mode 100644 index 00000000..f8fbe382 --- /dev/null +++ b/crates/walletkit-core/src/flamingo.rs @@ -0,0 +1,575 @@ +//! Attested `Flamingo` matching in preparation for zero-knowledge proof generation. +//! +//! This module deliberately knows nothing about Orb PCP storage. Its caller supplies the live +//! image and the credential material obtained through the platform's Oxide/OrbKit adapter. The +//! module owns assignment, attestation verification, sealing, transport, response opening, and +//! match-token verification. + +use std::sync::Arc; + +use async_trait::async_trait; +use flamingo_verifier_client::{ + Config, Error as ClientError, FaceVerifierClient, PcrMeasurement, + VerifiedAssignment, +}; +use flamingo_verifier_sealed_types::{ + FailureReason, MatchInputs, MatchResult, MATCH_PROTOCOL_VERSION, +}; +use thiserror::Error; + +// TODO: Replace all three PCRs with measurements from the approved enclave release. +// TODO: These should likely originate from one of the flamingo crates +const EXPECTED_ENCLAVE_PCR0: [u8; 48] = [0x42; 48]; +const EXPECTED_ENCLAVE_PCR1: [u8; 48] = [0x43; 48]; +const EXPECTED_ENCLAVE_PCR2: [u8; 48] = [0x44; 48]; + +/// `WalletKit`'s attested `Flamingo` match module. +/// +/// Keep this value alive across requests so the underlying HTTP client can retain transport state. +/// No WalletKit-owned sealing key is persisted: each verified assignment supplies the enclave's +/// attested public key, and the client creates fresh HPKE sealing material for the request. +#[derive(Debug, uniffi::Object)] +pub struct FlamingoMatcher { + client: FaceVerifierClient, +} + +/// Inputs for one attested `Flamingo` match. +/// +/// `credential_image` and `hashes_json` must come from the same enrolled Orb PCP. In particular, +/// `hashes_json` must contain the exact archive bytes, not parsed and reserialized JSON. +#[derive(Debug, uniffi::Record)] +pub struct FlamingoMatchRequest { + /// Raw liveness image bytes captured for this request. + pub live_image: Vec, + /// Raw `thumbnail.png` bytes decrypted from the enrolled Orb PCP. + pub credential_image: Vec, + /// Exact raw `hashes.json` bytes extracted from the enrolled Orb PCP. + pub hashes_json: Vec, + /// Optional second liveness frame for the `LightGuard` flow. + pub light_guard_image: Option>, + /// Raw challenge image bytes downloaded from the relying party. + pub challenge_image: Vec, + /// Minimum similarity required by the RP. Must be finite and between zero and one. + pub match_threshold: f32, +} + +/// A match token whose signing-key attestation and signature were verified. +/// +/// Foreign callers receive an opaque handle. The token and signing-key attestation remain +/// together in Rust for proof generation and eventual relay of the attestation to the RP. +#[derive(Debug, uniffi::Object)] +pub struct VerifiedMatchToken { + token: Vec, + signing_key_attestation: Vec, +} + +/// The outcome of the TEE match phase. +#[derive(Debug, uniffi::Enum)] +pub enum FlamingoMatchOutcome { + /// The enclave issued a token and `WalletKit` verified it against an attested signing key. + Matched(Arc), + /// The response reported a rejection. An unsigned rejection does not authenticate its sender. + Rejected(FlamingoMatchRejection), +} + +/// A rejection reason reported in an encrypted match response. +/// +/// The reason is unsigned; it is not proof that the attested enclave issued it. +#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)] +pub enum FlamingoMatchRejection { + /// The sealed inputs were malformed. + MalformedInputs, + /// The channel version was not supported by the enclave. + UnsupportedVersion, + /// The PCP hashes file was invalid or did not contain the thumbnail commitment. + InvalidHashesJson, + /// The credential image did not match the PCP thumbnail commitment. + ThumbnailHashMismatch, + /// At least one comparison scored below the requested threshold. + MatchBelowThreshold, + /// The enclave could not obtain a usable comparison score from the images. + ImageAnalysisFailed, +} + +/// Failures while configuring or performing a match request. +#[derive(Debug, Error, uniffi::Error)] +pub enum FlamingoError { + /// A caller-supplied value cannot form a valid match request. + #[error("invalid {attribute}: {reason}")] + InvalidInput { + /// Name of the invalid field. + attribute: String, + /// Why the value was rejected. + reason: String, + }, + /// The verifier configuration was not valid. + #[error("invalid Flamingo verifier configuration: {0}")] + Configuration(String), + /// Assignment, attestation, transport, channel opening, or token verification failed. + #[error("Flamingo verifier request failed: {0}")] + Verifier(String), +} + +#[async_trait] +trait MatchClient: Sync { + type Assignment: Send + Sync; + + async fn request_assignment(&self) -> Result; + + async fn request_match( + &self, + assignment: &Self::Assignment, + inputs: &MatchInputs, + ) -> Result; +} + +#[uniffi::export(async_runtime = "tokio")] +impl FlamingoMatcher { + /// Builds a matcher for a TEE host using `WalletKit`'s pinned enclave measurements. + /// + /// PCR0, PCR1, and PCR2 are placeholders that must be replaced before real matches can succeed. + /// Rust callers can supply custom pins through [`Self::with_measurements`]. + /// + /// # Errors + /// + /// Returns [`FlamingoError::Configuration`] when the configuration is invalid, or + /// [`FlamingoError::Verifier`] when the HTTP client cannot be constructed. + #[uniffi::constructor] + pub fn new(host_url: &str) -> Result { + Self::with_measurements(host_url, None) + } + + /// Performs the attested TEE match phase. + /// + /// A stale assignment is retried exactly once with a fresh assignment and freshly sealed + /// ciphertext. A reported rejection is returned without a retry. Only a successful match + /// carries a token verified against an attested signing key. + /// + /// # Errors + /// + /// Returns [`FlamingoError::InvalidInput`] before making a network request when a caller value + /// is unusable. Other failures are returned as [`FlamingoError::Verifier`]. + pub async fn perform_match( + &self, + request: FlamingoMatchRequest, + ) -> Result { + perform_match(&self.client, request).await + } +} + +impl FlamingoMatcher { + /// Returns the hardcoded PCR0/1/2 measurements used when no override is supplied. + #[must_use] + pub const fn default_measurements() -> [[u8; 48]; 3] { + [ + EXPECTED_ENCLAVE_PCR0, + EXPECTED_ENCLAVE_PCR1, + EXPECTED_ENCLAVE_PCR2, + ] + } + + /// Builds a matcher with custom PCR0/1/2 measurements, or the hardcoded defaults. + /// + /// Supply measurements from a trusted enclave build. `None` uses the same pins as + /// [`Self::new`]. All three measurements must be nonzero; debug enclaves are rejected. + /// + /// # Errors + /// Returns an error if a measurement is zero or the host/client configuration is invalid. + pub fn with_measurements( + host_url: &str, + measurements: Option<[[u8; 48]; 3]>, + ) -> Result { + let config = matcher_config(host_url, measurements)?; + let client = FaceVerifierClient::new(config) + .map_err(|error| FlamingoError::Verifier(error.to_string()))?; + Ok(Self { client }) + } +} + +impl FlamingoMatchRequest { + fn validate(&self) -> Result<(), FlamingoError> { + for (attribute, bytes) in [ + ("live_image", self.live_image.as_slice()), + ("credential_image", self.credential_image.as_slice()), + ("hashes_json", self.hashes_json.as_slice()), + ] { + if bytes.is_empty() { + return Err(FlamingoError::InvalidInput { + attribute: attribute.to_string(), + reason: "must not be empty".to_string(), + }); + } + } + + if self.challenge_image.is_empty() { + return Err(FlamingoError::InvalidInput { + attribute: "challenge_image".to_string(), + reason: "must not be empty".to_string(), + }); + } + + if self.light_guard_image.as_ref().is_some_and(Vec::is_empty) { + return Err(FlamingoError::InvalidInput { + attribute: "light_guard_image".to_string(), + reason: "must not be empty when provided".to_string(), + }); + } + + if !self.match_threshold.is_finite() + || !(0.0..=1.0).contains(&self.match_threshold) + { + return Err(FlamingoError::InvalidInput { + attribute: "match_threshold".to_string(), + reason: "must be finite and between 0 and 1 inclusive".to_string(), + }); + } + + Ok(()) + } + + fn into_inputs(self) -> MatchInputs { + MatchInputs { + version: MATCH_PROTOCOL_VERSION, + live_image: self.live_image, + credential_image: self.credential_image, + light_guard_image: self.light_guard_image, + hashes_json: self.hashes_json, + challenge_image: self.challenge_image, + match_threshold: self.match_threshold, + } + } +} + +impl VerifiedMatchToken { + /// Borrows the encoded COSE/CBOR token for proof generation. + #[must_use] + pub fn as_bytes(&self) -> &[u8] { + &self.token + } + + /// Borrows the signing-key attestation to relay alongside the generated proof. + #[must_use] + pub fn signing_key_attestation(&self) -> &[u8] { + &self.signing_key_attestation + } +} + +impl From for FlamingoMatchRejection { + fn from(value: FailureReason) -> Self { + match value { + FailureReason::MalformedInputs => Self::MalformedInputs, + FailureReason::UnsupportedVersion => Self::UnsupportedVersion, + FailureReason::InvalidHashesJson => Self::InvalidHashesJson, + FailureReason::ThumbnailHashMismatch => Self::ThumbnailHashMismatch, + FailureReason::MatchBelowThreshold => Self::MatchBelowThreshold, + FailureReason::ImageAnalysisFailed => Self::ImageAnalysisFailed, + } + } +} + +#[async_trait] +impl MatchClient for FaceVerifierClient { + type Assignment = VerifiedAssignment; + + async fn request_assignment(&self) -> Result { + self.request_assignment().await + } + + async fn request_match( + &self, + assignment: &Self::Assignment, + inputs: &MatchInputs, + ) -> Result { + self.request_match(assignment, inputs).await + } +} + +fn matcher_config( + host_url: &str, + measurements: Option<[[u8; 48]; 3]>, +) -> Result { + let measurements = + measurements.unwrap_or_else(FlamingoMatcher::default_measurements); + for (index, measurement) in measurements.iter().enumerate() { + if measurement.iter().all(|byte| *byte == 0) { + return Err(FlamingoError::Configuration(format!( + "PCR{index} must be nonzero; debug enclaves are not accepted" + ))); + } + } + Config::new( + host_url, + vec![vec![ + PcrMeasurement::new(0, measurements[0]), + PcrMeasurement::new(1, measurements[1]), + PcrMeasurement::new(2, measurements[2]), + ]], + ) + .map_err(|error| FlamingoError::Configuration(error.to_string())) +} + +async fn perform_match( + client: &C, + request: FlamingoMatchRequest, +) -> Result { + request.validate()?; + let request = request.into_inputs(); + let mut reassigned = false; + + loop { + let assignment = client + .request_assignment() + .await + .map_err(|error| verifier_error(&error))?; + + match client.request_match(&assignment, &request).await { + Ok(MatchResult::Success(statement)) => { + return Ok(FlamingoMatchOutcome::Matched(Arc::new( + VerifiedMatchToken { + token: statement.token.into_bytes(), + signing_key_attestation: statement.signing_key_attestation, + }, + ))); + } + Ok(MatchResult::Failed(reason)) => { + return Ok(FlamingoMatchOutcome::Rejected(reason.into())); + } + Err(ClientError::ReassignRequired) if !reassigned => reassigned = true, + Err(error) => return Err(verifier_error(&error)), + } + } +} + +fn verifier_error(error: &ClientError) -> FlamingoError { + FlamingoError::Verifier(error.to_string()) +} + +#[cfg(test)] +mod tests { + use std::{ + collections::VecDeque, + sync::{ + atomic::{AtomicUsize, Ordering}, + Mutex, + }, + }; + + use flamingo_verifier_client::Error as ClientError; + use flamingo_verifier_protocol::match_token::MatchToken; + use flamingo_verifier_sealed_types::{ + AttestedStatement, FailureReason, MatchInputs, MatchResult, + }; + + use super::{ + perform_match, FlamingoError, FlamingoMatchOutcome, FlamingoMatchRejection, + FlamingoMatchRequest, MatchClient, + }; + + struct FakeClient { + assignments: AtomicUsize, + results: Mutex>>, + } + + impl FakeClient { + fn new( + results: impl IntoIterator>, + ) -> Self { + Self { + assignments: AtomicUsize::new(0), + results: Mutex::new(results.into_iter().collect()), + } + } + } + + #[async_trait::async_trait] + impl MatchClient for FakeClient { + type Assignment = usize; + + async fn request_assignment(&self) -> Result { + Ok(self.assignments.fetch_add(1, Ordering::Relaxed)) + } + + async fn request_match( + &self, + _assignment: &Self::Assignment, + _inputs: &MatchInputs, + ) -> Result { + self.results + .lock() + .expect("fake result lock should not be poisoned") + .pop_front() + .expect("test should provide one result per request") + } + } + + fn request() -> FlamingoMatchRequest { + FlamingoMatchRequest { + live_image: b"live".to_vec(), + credential_image: b"credential".to_vec(), + hashes_json: br#"{"thumbnail.png":"00"}"#.to_vec(), + light_guard_image: None, + challenge_image: b"challenge".to_vec(), + match_threshold: 0.7, + } + } + + #[test] + fn custom_measurements_replace_all_default_pins() { + let measurements = [[1; 48], [2; 48], [3; 48]]; + let config = + super::matcher_config("https://verifier.example.com", Some(measurements)) + .unwrap(); + let json = serde_json::to_value(config).unwrap(); + assert_eq!(json["allowed_pcr_configs"].as_array().unwrap().len(), 1); + for (index, measurement) in measurements.iter().enumerate() { + assert_eq!(json["allowed_pcr_configs"][0][index]["index"], index); + assert_eq!( + json["allowed_pcr_configs"][0][index]["value"], + hex::encode(measurement) + ); + } + } + + #[test] + fn rejects_zero_custom_measurements() { + for index in 0..3 { + let mut measurements = [[1; 48], [2; 48], [3; 48]]; + measurements[index] = [0; 48]; + assert!(matches!( + super::FlamingoMatcher::with_measurements( + "https://verifier.example.com", + Some(measurements) + ), + Err(FlamingoError::Configuration(_)) + )); + } + } + + #[test] + fn rejects_an_invalid_host_url() { + assert!(matches!( + super::FlamingoMatcher::new("not a URL"), + Err(FlamingoError::Configuration(_)) + )); + } + + #[tokio::test] + async fn rejects_a_legacy_assignment_before_sending_images() { + let mut server = mockito::Server::new_async().await; + let assignment = server + .mock("POST", "/v1/enclave-assignment") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(r#"{"attestation":"YXR0ZXN0YXRpb24="}"#) + .expect(1) + .create_async() + .await; + let image_upload = server + .mock("POST", "/v1/matches") + .expect(0) + .create_async() + .await; + let matcher = super::FlamingoMatcher::with_measurements( + &server.url(), + Some([[1; 48], [2; 48], [3; 48]]), + ) + .unwrap(); + + let error = matcher.perform_match(request()).await.unwrap_err(); + + assert!(matches!(error, FlamingoError::Verifier(_))); + assignment.assert_async().await; + image_upload.assert_async().await; + drop(server); + } + + #[tokio::test] + async fn returns_a_verified_token_after_the_client_verifies_success() { + let client = FakeClient::new([Ok(MatchResult::Success(AttestedStatement { + token: MatchToken::from_bytes(b"signed-token".to_vec()), + signing_key_attestation: b"signing-key-attestation".to_vec(), + }))]); + + let outcome = perform_match(&client, request()) + .await + .expect("match should succeed"); + + let FlamingoMatchOutcome::Matched(token) = outcome else { + panic!("expected a matched outcome"); + }; + assert_eq!(token.as_bytes(), b"signed-token"); + assert_eq!(token.signing_key_attestation(), b"signing-key-attestation"); + assert_eq!(client.assignments.load(Ordering::Relaxed), 1); + } + + #[tokio::test] + async fn returns_a_typed_sealed_rejection() { + let client = FakeClient::new([Ok(MatchResult::Failed( + FailureReason::ThumbnailHashMismatch, + ))]); + + let outcome = perform_match(&client, request()) + .await + .expect("a sealed rejection is an outcome"); + + assert!(matches!( + outcome, + FlamingoMatchOutcome::Rejected( + FlamingoMatchRejection::ThumbnailHashMismatch + ) + )); + } + + #[tokio::test] + async fn reassigns_and_reseals_exactly_once() { + let client = FakeClient::new([ + Err(ClientError::ReassignRequired), + Ok(MatchResult::Failed(FailureReason::MatchBelowThreshold)), + ]); + + let outcome = perform_match(&client, request()) + .await + .expect("fresh assignment should recover the match request"); + + assert!(matches!( + outcome, + FlamingoMatchOutcome::Rejected(FlamingoMatchRejection::MatchBelowThreshold) + )); + assert_eq!(client.assignments.load(Ordering::Relaxed), 2); + } + + #[tokio::test] + async fn does_not_retry_a_second_stale_assignment() { + let client = FakeClient::new([ + Err(ClientError::ReassignRequired), + Err(ClientError::ReassignRequired), + ]); + + let error = perform_match(&client, request()) + .await + .expect_err("a second stale assignment should be surfaced"); + + assert!(matches!(error, FlamingoError::Verifier(_))); + assert_eq!(client.assignments.load(Ordering::Relaxed), 2); + } + + #[tokio::test] + async fn rejects_a_non_finite_threshold_before_assignment() { + let client = FakeClient::new([]); + let mut request = request(); + request.match_threshold = f32::NAN; + + let error = perform_match(&client, request).await.expect_err( + "NaN would bypass enclave comparisons and must be rejected locally", + ); + + assert!(matches!( + error, + FlamingoError::InvalidInput { + attribute, + .. + } if attribute == "match_threshold" + )); + assert_eq!(client.assignments.load(Ordering::Relaxed), 0); + } +} diff --git a/crates/walletkit-core/src/lib.rs b/crates/walletkit-core/src/lib.rs index 1f231bf4..ea9d3998 100644 --- a/crates/walletkit-core/src/lib.rs +++ b/crates/walletkit-core/src/lib.rs @@ -102,6 +102,10 @@ pub enum Region { Ap, } +/// Attested Flamingo matching in preparation for zero-knowledge proof generation. +#[cfg(not(target_arch = "wasm32"))] +pub mod flamingo; + /// Contains error outputs from `WalletKit` pub mod error; diff --git a/crates/walletkit-core/tests/authenticator_integration.rs b/crates/walletkit-core/tests/authenticator_integration.rs index 4b1c3c27..3b27d6b7 100644 --- a/crates/walletkit-core/tests/authenticator_integration.rs +++ b/crates/walletkit-core/tests/authenticator_integration.rs @@ -87,5 +87,5 @@ async fn test_authenticator_integration() { .await .unwrap(); let packed_account_data = authenticator.packed_account_data(); - println!("Created World ID with packed account data: {packed_account_data:?}",); + println!("Created World ID with packed account data: {packed_account_data:?}"); } diff --git a/deny.toml b/deny.toml index 213d40d2..a5a02e7b 100644 --- a/deny.toml +++ b/deny.toml @@ -36,6 +36,12 @@ allow = [ "Zlib", ] +[[licenses.clarify]] +name = "webpki" +version = "0.22.4" +expression = "ISC" +license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] + # Ignore unmaintained required crates warning [advisories] ignore = [ diff --git a/flake.lock b/flake.lock index cfb7a8b6..9986492f 100644 --- a/flake.lock +++ b/flake.lock @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1777605393, - "narHash": "sha256-Hjp0VOOHgHcTrX23iVvnfAudPcuCmfkfpQNFwv2v/ks=", + "lastModified": 1788332415, + "narHash": "sha256-BTFrmyh0oaVDsvA9NNw0YpbbeppTwU0t70iVx672Ew8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ff88db34cfa486fc4964a6991cab1678d82eee8c", + "rev": "860d7c835ab91bfc8972b67092f5f2db8e9390a0", "type": "github" }, "original": {