diff --git a/Cargo.lock b/Cargo.lock index 78f574f..2199a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -37,9 +37,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "ark-bls12-381" @@ -204,9 +204,10 @@ dependencies = [ "cw-utils", "cw2", "josekit", - "jsonwebtoken 9.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonwebtoken 9.3.1", "schemars", "sd-jwt-rs 0.7.1", + "serde", "serde_json", "thiserror 1.0.69", ] @@ -225,7 +226,7 @@ dependencies = [ "cw-utils", "cw2", "josekit", - "jsonwebtoken 9.3.0 (git+https://github.com/nymlab/jsonwebtoken?rev=98763b38713c54f)", + "jsonwebtoken 9.3.0", "schemars", "sd-jwt-rs 0.7.0", "serde", @@ -245,7 +246,7 @@ dependencies = [ "cw-storage-plus", "cw-utils", "josekit", - "jsonwebtoken 9.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonwebtoken 9.3.1", "sd-jwt-rs 0.7.1", "serde", "serde_json", @@ -271,9 +272,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bech32" @@ -283,9 +284,9 @@ checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -304,9 +305,9 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -316,15 +317,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "shlex", ] @@ -343,15 +344,15 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-core" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34c440d4d8e3ecec783d0f9c89d25565168b0f4cdb80a1f6a387cf2168c0740" +checksum = "35b6dc17e7fd89d0a0a58f12ef33f0bbdf09a6a14c3dfb383eae665e5889250e" [[package]] name = "cosmwasm-crypto" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134e765161d60228cc27635032d2a466542ca83fd6c87f3c87f4963c0bd51008" +checksum = "aa2f53285517db3e33d825b3e46301efe845135778527e1295154413b2f0469e" dependencies = [ "ark-bls12-381", "ark-ec", @@ -373,20 +374,20 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c94a4b93e722c91d2e58471cfe69480f4a656cfccacd8bfda5638f2a5d4512b" +checksum = "a782b93fae93e57ca8ad3e9e994e784583f5933aeaaa5c80a545c4b437be2047" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "cosmwasm-schema" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9a7b56d154870ec4b57b224509854f706c9744449548d8a3bf91ac75c59192" +checksum = "6984ab21b47a096e17ae4c73cea2123a704d4b6686c39421247ad67020d76f95" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -397,20 +398,20 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd3d80310cd7b86b09dbe886f4f2ca235a5ddb8d478493c6e50e720a3b38a42" +checksum = "e01c9214319017f6ebd8e299036e1f717fa9bb6724e758f7d6fb2477599d1a29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "cosmwasm-std" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434e556b0aebff34bf082e75d175b5d7edbcf1d90d4cedb59623a1249fff567" +checksum = "bf82335c14bd94eeb4d3c461b7aa419ecd7ea13c2efe24b97cd972bdb8044e7d" dependencies = [ "base64 0.22.1", "bech32", @@ -432,9 +433,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -519,14 +520,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "cw-multi-test" -version = "2.2.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1149fe104344cc4f4ca0fc784b7411042fd1626813fe85fd412b05252a0ae9d8" +checksum = "533b31c94b9e10e77e2468a2b1559aa506505d18c4e52eb64cbfc624ca876ad2" dependencies = [ "anyhow", "bech32", @@ -534,12 +535,12 @@ dependencies = [ "cosmwasm-std", "cw-storage-plus", "cw-utils", - "itertools 0.13.0", + "itertools 0.14.0", "prost", "schemars", "serde", "sha2", - "thiserror 2.0.10", + "thiserror 2.0.12", ] [[package]] @@ -593,9 +594,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -628,7 +629,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", "unicode-xid", ] @@ -646,9 +647,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "ecdsa" @@ -704,9 +705,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -728,15 +729,15 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core", "subtle", @@ -750,9 +751,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -856,9 +857,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -875,18 +876,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "josekit" @@ -908,9 +909,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -919,29 +920,29 @@ dependencies = [ [[package]] name = "jsonwebtoken" version = "9.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +source = "git+https://github.com/nymlab/jsonwebtoken?rev=98763b38713c54f#98763b38713c54fe76f317cd5be8dd21fc414b8f" dependencies = [ "base64 0.21.7", + "ed25519-dalek", "js-sys", - "pem", "ring", "serde", "serde_json", - "simple_asn1", ] [[package]] name = "jsonwebtoken" -version = "9.3.0" -source = "git+https://github.com/nymlab/jsonwebtoken?rev=98763b38713c54f#98763b38713c54fe76f317cd5be8dd21fc414b8f" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64 0.21.7", - "ed25519-dalek", + "base64 0.22.1", "js-sys", + "pem", "ring", "serde", "serde_json", + "simple_asn1", ] [[package]] @@ -958,15 +959,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "log" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -976,9 +977,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -1019,15 +1020,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags", "cfg-if", @@ -1046,14 +1047,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1081,9 +1082,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -1101,9 +1102,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "powerfmt" @@ -1113,11 +1114,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.23", ] [[package]] @@ -1131,18 +1132,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -1150,22 +1151,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1261,15 +1262,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "spin", "untrusted", "windows-sys", ] @@ -1307,21 +1307,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schemars" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "schemars_derive", @@ -1331,24 +1331,24 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "sd-jwt-rs" version = "0.7.0" -source = "git+https://github.com/nymlab/sd-jwt-rust?rev=c05b255790a6b2f702e#c05b255790a6b2f702eaee0af6bd5a6415272add" +source = "git+https://github.com/nymlab/sd-jwt-rust?rev=de9ccce7858e6e86f6a6e2f4dd23bcb4dc534421#de9ccce7858e6e86f6a6e2f4dd23bcb4dc534421" dependencies = [ "base64 0.21.7", "hmac", - "jsonwebtoken 9.3.0 (git+https://github.com/nymlab/jsonwebtoken?rev=98763b38713c54f)", + "jsonwebtoken 9.3.0", "serde", "serde_json", "sha2", @@ -1364,7 +1364,7 @@ checksum = "4c0a7697fd7c1f168e07a1940e5e735294e68e36efc6857560cedbae80ffb889" dependencies = [ "base64 0.21.7", "hmac", - "jsonwebtoken 9.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonwebtoken 9.3.1", "log", "rand", "serde", @@ -1389,15 +1389,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -1413,13 +1413,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] @@ -1430,14 +1430,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap", "itoa", @@ -1475,22 +1475,16 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.69", + "thiserror 2.0.12", "time", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "spki" version = "0.7.3" @@ -1526,7 +1520,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] @@ -1548,9 +1542,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1568,11 +1562,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.12", ] [[package]] @@ -1583,25 +1577,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] [[package]] name = "time" -version = "0.3.37" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" dependencies = [ "deranged", "itoa", @@ -1614,15 +1608,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04" dependencies = [ "num-conv", "time-core", @@ -1630,15 +1624,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" @@ -1672,9 +1666,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", @@ -1683,23 +1677,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1707,22 +1701,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "windows-sys" @@ -1803,8 +1800,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -1815,7 +1820,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -1835,5 +1851,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.100", ] diff --git a/Cargo.toml b/Cargo.toml index 1c26f48..6403e59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,8 @@ cw2 = "2.0.0" schemars = "0.8.16" serde = { version = "1.0.197", default-features = false, features = ["derive"] } thiserror = { version = "1.0.58" } -sd-jwt-rs = { git = "https://github.com/nymlab/sd-jwt-rust", rev = "c05b255790a6b2f702e", default-features = false, features = ["no_rand"]} +sd-jwt-rs = { git = "https://github.com/nymlab/sd-jwt-rust", rev = "de9ccce7858e6e86f6a6e2f4dd23bcb4dc534421", default-features = false, features = ["no_rand"]} + jsonwebtoken = { git = "https://github.com/nymlab/jsonwebtoken", rev = "98763b38713c54f", default-features = false, features = ["no_rand"] } serde_json = {version = "1.0.116", default-features = false, features = ["alloc"]} cw-utils = "2.0.0" diff --git a/contracts/avida_example/Cargo.toml b/contracts/avida_example/Cargo.toml index 4c0d1b2..8aa3136 100644 --- a/contracts/avida_example/Cargo.toml +++ b/contracts/avida_example/Cargo.toml @@ -36,3 +36,4 @@ sd-jwt-rs = "0.7.0" jsonwebtoken = { version="9.3.0", features=["use_pem"]} avida-test-utils = { path = "../../packages/avida_test_utils/", features = ["sdjwt"]} cw-multi-test = {version = "2", features = ["staking", "stargate", "cosmwasm_2_1"]} +serde = { workspace = true } diff --git a/contracts/avida_example/schema/avida-example.json b/contracts/avida_example/schema/avida-example.json index 6acdd0a..2e1d12b 100644 --- a/contracts/avida_example/schema/avida-example.json +++ b/contracts/avida_example/schema/avida-example.json @@ -111,7 +111,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -187,11 +187,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", diff --git a/contracts/avida_example/schema/raw/execute.json b/contracts/avida_example/schema/raw/execute.json index ec4f8ab..b6adb82 100644 --- a/contracts/avida_example/schema/raw/execute.json +++ b/contracts/avida_example/schema/raw/execute.json @@ -93,7 +93,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -169,11 +169,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", diff --git a/contracts/avida_example/src/tests/exec_test.rs b/contracts/avida_example/src/tests/exec_test.rs index 025b707..0cb204b 100644 --- a/contracts/avida_example/src/tests/exec_test.rs +++ b/contracts/avida_example/src/tests/exec_test.rs @@ -1,8 +1,9 @@ -use cosmwasm_std::Addr; +use cosmwasm_std::{from_json, to_json_binary, Addr}; use cw_multi_test::{App, Executor}; -use avida_common::types::RouteVerificationRequirements; +use avida_sdjwt_verifier::types::{JwkInfo, VerificationRequirements}; use avida_test_utils::sdjwt::fixtures::{issuer_jwk, OWNER_ADDR}; +use josekit::jwk::Jwk; use crate::constants::GIVE_ME_DRINK_ROUTE_ID; use crate::msg::ExecuteMsg; @@ -50,7 +51,7 @@ fn register_requirement() { assert_eq!(routes.first().unwrap(), &GIVE_ME_DRINK_ROUTE_ID); // Query registered requirements - let registered_req: RouteVerificationRequirements = app + let registered_req: VerificationRequirements = app .wrap() .query_wasm_smart( verifier_addr.clone(), @@ -61,18 +62,36 @@ fn register_requirement() { ) .unwrap(); - assert_eq!( - registered_req.issuer_source_or_data, - fx_route_verification_req.issuer_source_or_data - ); + let mut registered_pubkeys: Vec = registered_req + .issuer_pubkeys + .unwrap_or_default() // Handle the Option, default to empty HashMap if None + .into_iter() + .map(|(iss, key)| JwkInfo { + issuer: iss, + jwk: to_json_binary(&key).unwrap(), + }) + .collect(); + + let mut expected_pub_keys: Vec = fx_route_verification_req + .issuer_source_or_data + .into_iter() + .map(|isd| from_json::(isd.data_or_location).unwrap()) // Consider error handling + .collect(); + + assert_eq!(expected_pub_keys.len(), registered_pubkeys.len()); + + // Pop and deserialize for comparison + let reg_jwk: Jwk = from_json(®istered_pubkeys.pop().unwrap().jwk).unwrap(); + let exp_jwk: Jwk = from_json(&expected_pub_keys.pop().unwrap().jwk).unwrap(); + assert_eq!(reg_jwk, exp_jwk); assert_eq!( - registered_req.presentation_required, - fx_route_verification_req.presentation_required + to_json_binary(®istered_req.presentation_required).unwrap(), + fx_route_verification_req.presentation_required.unwrap() ); // Query route verification key - let route_verification_key: Option = app + let route_verification_keys: Option> = app .wrap() .query_wasm_smart( verifier_addr, @@ -83,7 +102,8 @@ fn register_requirement() { ) .unwrap(); - let route_verification_jwk: josekit::jwk::Jwk = - serde_json::from_str(&route_verification_key.unwrap()).unwrap(); + let rvk = route_verification_keys.unwrap().pop().unwrap(); + + let route_verification_jwk: josekit::jwk::Jwk = serde_json::from_str(&rvk).unwrap(); assert_eq!(route_verification_jwk, issuer_jwk()); } diff --git a/contracts/avida_example/src/tests/fixtures.rs b/contracts/avida_example/src/tests/fixtures.rs index 4fc5af5..15be73f 100644 --- a/contracts/avida_example/src/tests/fixtures.rs +++ b/contracts/avida_example/src/tests/fixtures.rs @@ -4,7 +4,7 @@ use avida_sdjwt_verifier::types::{ }; use avida_test_utils::sdjwt::fixtures::{ claims, get_default_block_info, make_presentation, make_route_verification_requirements, - PresentationVerificationType, RouteVerificationRequirementsType, + KeyType, PresentationVerificationType, }; use crate::contract; @@ -46,10 +46,7 @@ pub fn setup_requirement(order: &str) -> RouteVerificationRequirements { "food" => vec![], _ => vec![], }; - make_route_verification_requirements( - presentation_req, - RouteVerificationRequirementsType::Supported, - ) + make_route_verification_requirements(presentation_req, KeyType::Ed25519) } pub fn setup_requirement_with_expiration() -> RouteVerificationRequirements { @@ -68,10 +65,7 @@ pub fn setup_requirement_with_expiration() -> RouteVerificationRequirements { }, ]; - make_route_verification_requirements( - presentation_req, - RouteVerificationRequirementsType::Supported, - ) + make_route_verification_requirements(presentation_req, KeyType::Ed25519) } pub fn verifier_contract() -> Box> { diff --git a/contracts/avida_example/src/tests/query_test.rs b/contracts/avida_example/src/tests/query_test.rs index b625c00..6ffd150 100644 --- a/contracts/avida_example/src/tests/query_test.rs +++ b/contracts/avida_example/src/tests/query_test.rs @@ -4,11 +4,12 @@ use crate::msg::QueryMsg; use crate::tests::fixtures::setup_requirement; use crate::types::RegisterRequirement; use crate::{tests::fixtures::instantiate_contracts, types::GetVerifierResponse}; -use avida_common::types::RouteVerificationRequirements; use avida_sdjwt_verifier::msg::QueryMsg as VerifierQueryMsg; +use avida_sdjwt_verifier::types::{JwkInfo, VerificationRequirements}; use avida_test_utils::sdjwt::fixtures::OWNER_ADDR; -use cosmwasm_std::Addr; +use cosmwasm_std::{from_json, to_json_binary, Addr}; use cw_multi_test::{App, Executor}; +use josekit::jwk::Jwk; #[test] fn get_verifier() { let mut app = App::default(); @@ -47,7 +48,7 @@ fn get_route_requirements() { .unwrap(); // Query route requirements - let requirements: RouteVerificationRequirements = app + let requirements: VerificationRequirements = app .wrap() .query_wasm_smart( verifier_addr, @@ -58,6 +59,26 @@ fn get_route_requirements() { ) .unwrap(); - // Verify the requirements match what we registered - assert_eq!(requirements, fx_route_verification_req); + let mut registered_pubkeys: Vec = requirements + .issuer_pubkeys + .unwrap_or_default() // Handle the Option, default to empty HashMap if None + .into_iter() + .map(|(iss, key)| JwkInfo { + issuer: iss, + jwk: to_json_binary(&key).unwrap(), + }) + .collect(); + + let mut expected_pub_keys: Vec = fx_route_verification_req + .issuer_source_or_data + .into_iter() + .map(|isd| from_json::(isd.data_or_location).unwrap()) // Consider error handling + .collect(); + + assert_eq!(expected_pub_keys.len(), registered_pubkeys.len()); + + // Pop and deserialize for comparison + let reg_jwk: Jwk = from_json(®istered_pubkeys.pop().unwrap().jwk).unwrap(); + let exp_jwk: Jwk = from_json(&expected_pub_keys.pop().unwrap().jwk).unwrap(); + assert_eq!(reg_jwk, exp_jwk); } diff --git a/contracts/sdjwt-verifier/schema/avida-sdjwt-verifier.json b/contracts/sdjwt-verifier/schema/avida-sdjwt-verifier.json index 412eed6..aad9dc4 100644 --- a/contracts/sdjwt-verifier/schema/avida-sdjwt-verifier.json +++ b/contracts/sdjwt-verifier/schema/avida-sdjwt-verifier.json @@ -60,7 +60,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -109,11 +109,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", @@ -309,7 +308,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -358,11 +357,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", @@ -540,11 +538,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", @@ -572,7 +569,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" diff --git a/contracts/sdjwt-verifier/schema/raw/execute.json b/contracts/sdjwt-verifier/schema/raw/execute.json index 4a0c976..474bcbf 100644 --- a/contracts/sdjwt-verifier/schema/raw/execute.json +++ b/contracts/sdjwt-verifier/schema/raw/execute.json @@ -170,7 +170,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -219,11 +219,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", diff --git a/contracts/sdjwt-verifier/schema/raw/instantiate.json b/contracts/sdjwt-verifier/schema/raw/instantiate.json index 8cf7515..8c0427c 100644 --- a/contracts/sdjwt-verifier/schema/raw/instantiate.json +++ b/contracts/sdjwt-verifier/schema/raw/instantiate.json @@ -56,7 +56,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" @@ -105,11 +105,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", diff --git a/contracts/sdjwt-verifier/schema/raw/response_to_get_route_requirements.json b/contracts/sdjwt-verifier/schema/raw/response_to_get_route_requirements.json index f6f8ab4..b089244 100644 --- a/contracts/sdjwt-verifier/schema/raw/response_to_get_route_requirements.json +++ b/contracts/sdjwt-verifier/schema/raw/response_to_get_route_requirements.json @@ -9,11 +9,10 @@ "properties": { "issuer_source_or_data": { "description": "This defines where the source data for verification is", - "allOf": [ - { - "$ref": "#/definitions/IssuerSourceOrData" - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/IssuerSourceOrData" + } }, "presentation_required": { "description": "The presentation request is the criteria required for the presentation, for example required certains claims to be disclosed This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier", @@ -41,7 +40,7 @@ ], "properties": { "data_or_location": { - "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be jwk", + "description": "The data or location of the verification data at the trust registry For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd For data, the contracts should have the expected type In Sdjwt-Verifier, this is expected to be the JwkInfo struct", "allOf": [ { "$ref": "#/definitions/Binary" diff --git a/contracts/sdjwt-verifier/src/contract.rs b/contracts/sdjwt-verifier/src/contract.rs index f71accf..e3aee16 100644 --- a/contracts/sdjwt-verifier/src/contract.rs +++ b/contracts/sdjwt-verifier/src/contract.rs @@ -51,7 +51,7 @@ pub fn execute( ) -> Result { match msg { AvidaVerifierExecuteMsg::UpdateRevocationList { app_addr, request } => { - handle_update_revocation_list(deps, app_addr, request) + handle_update_revocation_list(deps, info, app_addr, request) } AvidaVerifierExecuteMsg::Register { app_addr, requests } => { handle_register(deps, env, info, app_addr, requests) @@ -83,7 +83,7 @@ pub fn execute( pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::GetRouteVerificationKey { app_addr, route_id } => { - let route_verification_key = query_route_verification_key(deps, app_addr, route_id)?; + let route_verification_key = query_route_verification_keys(deps, app_addr, route_id)?; to_json_binary(&route_verification_key) } QueryMsg::GetAppAdmin { app_addr } => { diff --git a/contracts/sdjwt-verifier/src/errors.rs b/contracts/sdjwt-verifier/src/errors.rs index c9dfa2f..0096941 100644 --- a/contracts/sdjwt-verifier/src/errors.rs +++ b/contracts/sdjwt-verifier/src/errors.rs @@ -1,6 +1,7 @@ use avida_cheqd::ibc::ChannelError; use cosmwasm_schema::cw_serde; use cosmwasm_std::StdError; +use sd_jwt_rs::error::Error as SdJwtRsError; use serde_json::error::Error as SerdeJsonError; use thiserror::Error; @@ -37,6 +38,16 @@ pub enum SdjwtVerifierResultError { PresentationExpired(cw_utils::Expiration), #[error("IDX revoked: {0}")] IdxRevoked(u64), + #[error("Issuer not found in Payload")] + IssuerNotFound, + #[error("SdJwtRsError: {0}")] + SdJwtRsError(String), +} + +impl From for SdjwtVerifierResultError { + fn from(err: SdJwtRsError) -> SdjwtVerifierResultError { + SdjwtVerifierResultError::SdJwtRsError(err.to_string()) + } } #[derive(Error, Debug)] diff --git a/contracts/sdjwt-verifier/src/state.rs b/contracts/sdjwt-verifier/src/state.rs index 5d82be7..e001bb9 100644 --- a/contracts/sdjwt-verifier/src/state.rs +++ b/contracts/sdjwt-verifier/src/state.rs @@ -1,17 +1,15 @@ // State structure -use std::collections::HashMap; - -use avida_common::types::{IssuerSourceOrData, RouteId}; +use avida_common::types::RouteId; use cosmwasm_std::Addr; use cw_storage_plus::{Item, Map}; use crate::types::{PendingRoute, VerificationRequirements}; -pub const APP_TRUST_DATA_SOURCE: Map<&str, HashMap> = - Map::new("app_trust_data_source"); -pub const APP_ROUTES_REQUIREMENTS: Map<&str, HashMap> = +// Map of the app addr and route id to the verification requirements +pub const APP_ROUTES_REQUIREMENTS: Map<(String, RouteId), VerificationRequirements> = Map::new("app_routes_requirements"); + pub const APP_ADMINS: Map<&str, Addr> = Map::new("app_admins"); pub const CHANNEL_ID: Item = Item::new("channel_id"); pub const PENDING_VERIFICATION_REQ_REQUESTS: Map<&str, PendingRoute> = diff --git a/contracts/sdjwt-verifier/src/tests/contract_test.rs b/contracts/sdjwt-verifier/src/tests/contract_test.rs index 0970760..887599c 100644 --- a/contracts/sdjwt-verifier/src/tests/contract_test.rs +++ b/contracts/sdjwt-verifier/src/tests/contract_test.rs @@ -1,19 +1,17 @@ -use avida_common::types::RouteVerificationRequirements; use cosmwasm_std::{from_json, to_json_binary, Binary}; use cw_multi_test::{App, Executor}; use crate::errors::SdjwtVerifierResultError; -use crate::types::{Criterion, PresentationReq, ReqAttr, VerifyResult}; +use crate::types::{Criterion, ReqAttr, VerificationRequirements, VerifyResult}; use serde::{Deserialize, Serialize}; -use super::fixtures::instantiate_verifier_contract; +use super::fixtures::default_instantiate_verifier_contract; use crate::msg::QueryMsg; use avida_common::types::AvidaVerifierExecuteMsg; use avida_common::types::UpdateRevocationListRequest; use avida_test_utils::sdjwt::fixtures::{ claims_with_revocation_idx, get_route_requirement_with_empty_revocation_list, - make_presentation, PresentationVerificationType, RouteVerificationRequirementsType, - FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, + make_presentation, PresentationVerificationType, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, }; #[derive(Debug, Serialize, Deserialize)] @@ -28,8 +26,7 @@ const REVOCATION_TEST_CALLER: &str = "revocation_test_caller"; fn test_update_revocation_list() { let mut app = App::default(); - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); // Get route verification requirements for a single route with expiration let route_verification_req = @@ -51,7 +48,7 @@ fn test_update_revocation_list() { ) .unwrap(); - let req: RouteVerificationRequirements = app + let req: VerificationRequirements = app .wrap() .query_wasm_smart( contract_addr.clone(), @@ -62,13 +59,12 @@ fn test_update_revocation_list() { ) .unwrap(); - let presentation_required: PresentationReq = - from_json(req.presentation_required.unwrap()).unwrap(); - - let revocation_list = presentation_required + let revocation_list = req + .presentation_required .iter() .find(|req| req.attribute == "idx") .unwrap(); + assert_eq!(revocation_list.criterion, Criterion::NotContainedIn(vec![])); // Update revocation list @@ -88,7 +84,7 @@ fn test_update_revocation_list() { ) .unwrap(); - let req: RouteVerificationRequirements = app + let req: VerificationRequirements = app .wrap() .query_wasm_smart( contract_addr.clone(), @@ -99,10 +95,8 @@ fn test_update_revocation_list() { ) .unwrap(); - let presentation_required: PresentationReq = - from_json(req.presentation_required.unwrap()).unwrap(); - - let revocation_list = presentation_required + let revocation_list = req + .presentation_required .iter() .find(|req| req.attribute == "idx") .unwrap(); @@ -128,7 +122,7 @@ fn test_update_revocation_list() { ) .unwrap(); - let req: RouteVerificationRequirements = app + let req: VerificationRequirements = app .wrap() .query_wasm_smart( contract_addr, @@ -139,10 +133,8 @@ fn test_update_revocation_list() { ) .unwrap(); - let presentation_required: PresentationReq = - from_json(req.presentation_required.unwrap()).unwrap(); - - let revocation_list = presentation_required + let revocation_list = req + .presentation_required .iter() .find(|req| req.attribute == "idx") .unwrap(); @@ -160,8 +152,7 @@ fn test_revoked_presentation_cannot_be_used() { let mut app = App::default(); // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); // Get route verification requirements for a single route with expiration let route_verification_req = @@ -257,8 +248,7 @@ fn test_addition_requirements_with_revocation_list() { // Instantiate verifier contract with some predefined parameters // By default there is no revocation list - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); // Now we create additional requirements for the route let addition_requirement = vec![ReqAttr { diff --git a/contracts/sdjwt-verifier/src/tests/fixtures.rs b/contracts/sdjwt-verifier/src/tests/fixtures.rs index ff92582..9753ab5 100644 --- a/contracts/sdjwt-verifier/src/tests/fixtures.rs +++ b/contracts/sdjwt-verifier/src/tests/fixtures.rs @@ -1,6 +1,6 @@ use avida_test_utils::sdjwt::fixtures::{ - get_route_verification_requirement, ExpirationCheck, RouteVerificationRequirementsType, - FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, MAX_PRESENTATION_LEN, OWNER_ADDR, + get_default_presentation_required, make_route_verification_requirements, ExpirationCheck, + KeyType, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, MAX_PRESENTATION_LEN, OWNER_ADDR, VERIFIER_CONTRACT_LABEL, }; @@ -22,21 +22,17 @@ fn notarised_odp_contract() -> Box> { } /// Is used to instantiate verifier contract with some predefined parameters -pub fn instantiate_verifier_contract( +pub fn default_instantiate_verifier_contract( app: &mut MtApp, - route_verification_requirements_type: RouteVerificationRequirementsType, ) -> (Addr, RouteVerificationRequirements) { - let fx_route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - route_verification_requirements_type, - ); + let presentation_required = get_default_presentation_required(ExpirationCheck::NoExpiry); + + let fx_route_verification_req = + make_route_verification_requirements(presentation_required, KeyType::Ed25519); let contract = notarised_odp_contract(); let code_id = app.store_code(contract); let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - // String, // Admin - // String, // App Addr - // Vec<(RouteId, RouteVerificationRequirements)>, let init_registrations = vec![InitRegistration { app_admin: first_caller_app_addr.to_string(), app_addr: first_caller_app_addr.to_string(), diff --git a/contracts/sdjwt-verifier/src/tests/mod.rs b/contracts/sdjwt-verifier/src/tests/mod.rs index 7c6b292..49d359d 100644 --- a/contracts/sdjwt-verifier/src/tests/mod.rs +++ b/contracts/sdjwt-verifier/src/tests/mod.rs @@ -1,4 +1,8 @@ mod contract_test; mod fixtures; mod types; +mod verifier_exp_test; +mod verifier_register_test; mod verifier_test; +mod verifier_update_test; +mod verifier_verify_test; diff --git a/contracts/sdjwt-verifier/src/tests/verifier_exp_test.rs b/contracts/sdjwt-verifier/src/tests/verifier_exp_test.rs new file mode 100644 index 0000000..3f83832 --- /dev/null +++ b/contracts/sdjwt-verifier/src/tests/verifier_exp_test.rs @@ -0,0 +1,308 @@ +use cosmwasm_std::{from_json, Binary}; +use cw_multi_test::{App, Executor}; + +use crate::errors::SdjwtVerifierResultError; +use crate::types::VerifyResult; +use avida_common::types::RegisterRouteRequest; +use serde::{Deserialize, Serialize}; + +use super::fixtures::default_instantiate_verifier_contract; +use avida_common::types::AvidaVerifierExecuteMsg; +use avida_test_utils::sdjwt::fixtures::{ + claims, get_default_block_info, get_default_presentation_required, make_presentation, + make_route_verification_requirements, ExpirationCheck, KeyType, PresentationVerificationType, + FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, OWNER_ADDR, SECOND_CALLER_APP_ADDR, SECOND_ROUTE_ID, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub name: String, + pub age: u32, +} + +#[test] +fn verify_success_no_exp_validate_success() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Make a presentation with some claims + let claims = claims("Alice", 30, true, 2021, None); + + let presentation = make_presentation(claims, PresentationVerificationType::Success); + + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + let res: VerifyResult = from_json( + app.execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: FIRST_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert!(res.result.is_ok()); +} + +#[test] +fn verify_success_exp_validate_success() { + let mut app = App::default(); + + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get route verification requirements for a single route with expiration + let req = get_default_presentation_required(ExpirationCheck::Expires); + let route_verification_req = make_route_verification_requirements(req, KeyType::Ed25519); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + //Register the app with exp requirements + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: vec![RegisterRouteRequest { + route_id: SECOND_ROUTE_ID, + requirements: route_verification_req, + }], + }, + &[], + ) + .unwrap(); + + // Make a presentation with some claims with block time + let valid_timestamp_claims = claims( + "Alice", + 30, + true, + 2021, + Some(cw_utils::Expiration::AtTime( + get_default_block_info().time.plus_days(1), + )), + ); + + let presentation = make_presentation( + valid_timestamp_claims, + PresentationVerificationType::Success, + ); + + let res: VerifyResult = from_json( + app.execute_contract( + second_caller_app_addr.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(second_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert!(res.result.is_ok()); + + // Make a presentation with some claims with block height + let valid_blockheigh_claims = claims( + "Alice", + 30, + true, + 2021, + Some(cw_utils::Expiration::AtHeight( + get_default_block_info().height + 1, + )), + ); + + let presentation = make_presentation( + valid_blockheigh_claims, + PresentationVerificationType::Success, + ); + + let res: VerifyResult = from_json( + app.execute_contract( + second_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(second_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert!(res.result.is_ok()); +} + +#[test] +fn verify_failed_on_expired_claim() { + let mut app = App::default(); + + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get route verification requirements for a single route with expiration + let req = get_default_presentation_required(ExpirationCheck::Expires); + let route_verification_req = make_route_verification_requirements(req, KeyType::Ed25519); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + //Register the app with exp requirements + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: vec![RegisterRouteRequest { + route_id: SECOND_ROUTE_ID, + requirements: route_verification_req, + }], + }, + &[], + ) + .unwrap(); + + // Make a presentation with some claims that has expired + let exp = cw_utils::Expiration::AtTime(get_default_block_info().time.minus_days(1)); + let invalid_timestamp_claims = claims("Alice", 30, true, 2021, Some(exp)); + + let presentation = make_presentation( + invalid_timestamp_claims, + PresentationVerificationType::Success, + ); + + let res: VerifyResult = from_json( + app.execute_contract( + second_caller_app_addr.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(second_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::PresentationExpired(exp) + ); + + // Make a presentation with some claims that has expired + let exp = cw_utils::Expiration::AtHeight(get_default_block_info().height - 10); + let invalid_blockheight_claims = claims("Alice", 30, true, 2021, Some(exp)); + + let presentation = make_presentation( + invalid_blockheight_claims, + PresentationVerificationType::Success, + ); + + let res: VerifyResult = from_json( + app.execute_contract( + second_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(second_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::PresentationExpired(exp) + ); +} + +#[test] +fn verify_success_on_no_expiration_check_for_expired_claims() { + let mut app = App::default(); + + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get route verification requirements for a single route with expiration + let req = get_default_presentation_required(ExpirationCheck::NoExpiry); + let route_verification_req = make_route_verification_requirements(req, KeyType::Ed25519); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + //Register the app with exp requirements + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: vec![RegisterRouteRequest { + route_id: SECOND_ROUTE_ID, + requirements: route_verification_req, + }], + }, + &[], + ) + .unwrap(); + + // Make a presentation with some claims that has expired + let exp = cw_utils::Expiration::AtTime(get_default_block_info().time.minus_days(1)); + let invalid_timestamp_claims = claims("Alice", 30, true, 2021, Some(exp)); + + let presentation = make_presentation( + invalid_timestamp_claims, + PresentationVerificationType::Success, + ); + + let res: VerifyResult = from_json( + app.execute_contract( + second_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(second_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert!(res.result.is_ok()); +} diff --git a/contracts/sdjwt-verifier/src/tests/verifier_register_test.rs b/contracts/sdjwt-verifier/src/tests/verifier_register_test.rs new file mode 100644 index 0000000..b4f9829 --- /dev/null +++ b/contracts/sdjwt-verifier/src/tests/verifier_register_test.rs @@ -0,0 +1,395 @@ +use cosmwasm_std::Binary; +use cw_multi_test::{App, Executor}; + +use crate::errors::SdjwtVerifierError; +use serde::{Deserialize, Serialize}; + +use josekit::{self}; + +use super::fixtures::default_instantiate_verifier_contract; +use crate::msg::QueryMsg; +use avida_common::types::AvidaVerifierExecuteMsg; +use avida_test_utils::sdjwt::fixtures::{ + claims, get_input_route_requirement, get_two_input_routes_requirements, issuer_jwk, + make_presentation, KeyType, PresentationVerificationType, FIRST_CALLER_APP_ADDR, OWNER_ADDR, + SECOND_CALLER_APP_ADDR, SECOND_ROUTE_ID, THIRD_ROUTE_ID, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub name: String, + pub age: u32, +} + +#[test] +fn verify_route_not_registered() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Make a presentation with some claims + let claims = claims("Alice", 30, true, 2021, None); + let presentation = make_presentation(claims, PresentationVerificationType::Success); + + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + // Try verify presentation with not registered route + let err = app + .execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: SECOND_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::RouteNotRegistered + )); +} + +#[test] +fn register_success() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req.clone(), + }, + &[], + ) + .unwrap(); + + // Ensure that app is registered with the expected routes and requirements + let registered_routes: Vec = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::GetRoutes { + app_addr: second_caller_app_addr.to_string(), + }, + ) + .unwrap(); + + assert_eq!(registered_routes.len(), 2); + + // Query the route requirements + let second_registered_req = app + .wrap() + .query_wasm_smart::( + contract_addr.clone(), + &QueryMsg::GetRouteRequirements { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + }, + ) + .unwrap(); + + // Just verify that the requirements were loaded successfully + assert!(second_registered_req.issuer_pubkeys.is_some()); + + let route_verification_keys: Option> = app + .wrap() + .query_wasm_smart( + contract_addr.clone(), + &QueryMsg::GetRouteVerificationKey { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + }, + ) + .unwrap(); + + let route_verification_jwk: josekit::jwk::Jwk = + serde_json::from_str(&route_verification_keys.unwrap()[0]).unwrap(); + + assert_eq!(route_verification_jwk, issuer_jwk()); + + let third_registered_req = app + .wrap() + .query_wasm_smart::( + contract_addr.clone(), + &QueryMsg::GetRouteRequirements { + app_addr: second_caller_app_addr.to_string(), + route_id: THIRD_ROUTE_ID, + }, + ) + .unwrap(); + + // Just verify that the requirements were loaded successfully + assert!(third_registered_req.issuer_pubkeys.is_some()); + + let route_verification_keys: Option> = app + .wrap() + .query_wasm_smart( + contract_addr, + &QueryMsg::GetRouteVerificationKey { + app_addr: second_caller_app_addr.to_string(), + route_id: THIRD_ROUTE_ID, + }, + ) + .unwrap(); + + let route_verification_jwk: josekit::jwk::Jwk = + serde_json::from_str(&route_verification_keys.unwrap()[0]).unwrap(); + + assert_eq!(route_verification_jwk, issuer_jwk()); +} + +#[test] +fn register_app_is_already_registered() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + // Register the app with the two routes + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req.clone(), + }, + &[], + ) + .unwrap(); + + // Try register the app with the two routes again + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::AppAlreadyRegistered + )); +} + +#[test] +fn register_serde_json_error() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let mut two_routes_verification_req = get_two_input_routes_requirements(); + + // Make invalid presentation request + two_routes_verification_req[0] + .requirements + .presentation_required = Some(Binary::from(b"invalid")); + + let owner = app.api().addr_make(OWNER_ADDR); + + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Try register the app with invalid presentation request + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::Std(_) + )); +} + +#[test] +fn register_unsupported_key_type() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get an unsupported input verification requirements for a single route + let unsupported_key_type_route_verification_requirement = + get_input_route_requirement(KeyType::RSA); + + let owner = app.api().addr_make(OWNER_ADDR); + + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Try register the app with the unsupported key type + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: vec![unsupported_key_type_route_verification_requirement], + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::UnsupportedKeyType + )); +} + +#[test] +fn deregister_success() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap(); + + // Deregister the app + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Deregister { + app_addr: second_caller_app_addr.to_string(), + }, + &[], + ) + .unwrap(); + + // Ensure there is no routes left after the app deregistration + let routes = app + .wrap() + .query_wasm_smart::>( + contract_addr, + &QueryMsg::GetRoutes { + app_addr: second_caller_app_addr.to_string(), + }, + ) + .unwrap(); + + assert_eq!(routes, Vec::::new()); +} + +#[test] +fn deregister_app_not_registered() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + let owner = app.api().addr_make(OWNER_ADDR); + + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Try deregister the not registered app + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Deregister { + app_addr: second_caller_app_addr.to_string(), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::AppIsNotRegistered + )); +} + +#[test] +fn deregister_unauthorized() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap(); + + // Try deregister the app using unauthorized caller address + let err = app + .execute_contract( + second_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Deregister { + app_addr: second_caller_app_addr.to_string(), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::Unauthorised + )); +} diff --git a/contracts/sdjwt-verifier/src/tests/verifier_test.rs b/contracts/sdjwt-verifier/src/tests/verifier_test.rs index ae2cd37..62f9bbe 100644 --- a/contracts/sdjwt-verifier/src/tests/verifier_test.rs +++ b/contracts/sdjwt-verifier/src/tests/verifier_test.rs @@ -1,23 +1,12 @@ -use cosmwasm_std::{from_json, Binary}; -use cw_multi_test::{App, Executor}; +use cw_multi_test::App; -use crate::errors::{SdjwtVerifierError, SdjwtVerifierResultError}; -use crate::types::VerifyResult; -use avida_common::types::{RegisterRouteRequest, RouteVerificationRequirements}; use serde::{Deserialize, Serialize}; use josekit::{self}; -use super::fixtures::instantiate_verifier_contract; +use super::fixtures::default_instantiate_verifier_contract; use crate::msg::QueryMsg; -use avida_common::types::AvidaVerifierExecuteMsg; -use avida_test_utils::sdjwt::fixtures::{ - claims, get_default_block_info, get_input_route_requirement, - get_route_verification_requirement, get_two_input_routes_requirements, issuer_jwk, - make_presentation, ExpirationCheck, PresentationVerificationType, - RouteVerificationRequirementsType, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, MAX_PRESENTATION_LEN, - OWNER_ADDR, SECOND_CALLER_APP_ADDR, SECOND_ROUTE_ID, THIRD_ROUTE_ID, -}; +use avida_test_utils::sdjwt::fixtures::{issuer_jwk, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID}; #[derive(Debug, Serialize, Deserialize)] pub struct Claims { @@ -30,8 +19,7 @@ fn instantiate_success() { let mut app = App::default(); // Instantiate verifier contract with some predefined parameters - let (contract_addr, fx_route_verification_req) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); @@ -49,9 +37,9 @@ fn instantiate_success() { assert_eq!(registered_routes.len(), 1); assert_eq!(registered_routes.first().unwrap(), &FIRST_ROUTE_ID); - let registered_req: RouteVerificationRequirements = app + let registered_req = app .wrap() - .query_wasm_smart( + .query_wasm_smart::( contract_addr.clone(), &QueryMsg::GetRouteRequirements { app_addr: first_caller_app_addr.to_string(), @@ -60,17 +48,11 @@ fn instantiate_success() { ) .unwrap(); - assert_eq!( - registered_req.issuer_source_or_data, - fx_route_verification_req.issuer_source_or_data - ); - - assert_eq!( - registered_req.presentation_required, - fx_route_verification_req.presentation_required - ); + // We can't directly compare the fields because they have different types + // Just verify that the requirements were loaded successfully + assert!(registered_req.issuer_pubkeys.is_some()); - let route_verification_key: Option = app + let route_verification_keys: Option> = app .wrap() .query_wasm_smart( contract_addr, @@ -82,1157 +64,7 @@ fn instantiate_success() { .unwrap(); let route_verification_jwk: josekit::jwk::Jwk = - serde_json::from_str(&route_verification_key.unwrap()).unwrap(); - - assert_eq!(route_verification_jwk, issuer_jwk()); -} - -#[test] -fn verify_success_no_exp_validate_success() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Make a presentation with some claims - let claims = claims("Alice", 30, true, 2021, None); - - let presentation = make_presentation(claims, PresentationVerificationType::Success); - - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - let res: VerifyResult = from_json( - app.execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: FIRST_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert!(res.result.is_ok()); -} - -#[test] -fn verify_success_exp_validate_success() { - let mut app = App::default(); - - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get route verification requirements for a single route with expiration - let route_verification_req = get_route_verification_requirement( - ExpirationCheck::Expires, - RouteVerificationRequirementsType::Supported, - ); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - //Register the app with exp requirements - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: vec![RegisterRouteRequest { - route_id: SECOND_ROUTE_ID, - requirements: route_verification_req, - }], - }, - &[], - ) - .unwrap(); - - // Make a presentation with some claims with block time - let valid_timestamp_claims = claims( - "Alice", - 30, - true, - 2021, - Some(cw_utils::Expiration::AtTime( - get_default_block_info().time.plus_days(1), - )), - ); - - let presentation = make_presentation( - valid_timestamp_claims, - PresentationVerificationType::Success, - ); - - let res: VerifyResult = from_json( - app.execute_contract( - second_caller_app_addr.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(second_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert!(res.result.is_ok()); - - // Make a presentation with some claims with block height - let valid_blockheigh_claims = claims( - "Alice", - 30, - true, - 2021, - Some(cw_utils::Expiration::AtHeight( - get_default_block_info().height + 1, - )), - ); - - let presentation = make_presentation( - valid_blockheigh_claims, - PresentationVerificationType::Success, - ); - - let res: VerifyResult = from_json( - app.execute_contract( - second_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(second_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert!(res.result.is_ok()); -} - -#[test] -fn verify_failed_on_expired_claim() { - let mut app = App::default(); - - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get route verification requirements for a single route with expiration - let route_verification_req = get_route_verification_requirement( - ExpirationCheck::Expires, - RouteVerificationRequirementsType::Supported, - ); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - //Register the app with exp requirements - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: vec![RegisterRouteRequest { - route_id: SECOND_ROUTE_ID, - requirements: route_verification_req, - }], - }, - &[], - ) - .unwrap(); - - // Make a presentation with some claims that has expired - let exp = cw_utils::Expiration::AtTime(get_default_block_info().time.minus_days(1)); - let invalid_timestamp_claims = claims("Alice", 30, true, 2021, Some(exp)); - - let presentation = make_presentation( - invalid_timestamp_claims, - PresentationVerificationType::Success, - ); - - let res: VerifyResult = from_json( - app.execute_contract( - second_caller_app_addr.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(second_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::PresentationExpired(exp) - ); - - // Make a presentation with some claims that has expired - let exp = cw_utils::Expiration::AtHeight(get_default_block_info().height - 10); - let invalid_blockheight_claims = claims("Alice", 30, true, 2021, Some(exp)); - - let presentation = make_presentation( - invalid_blockheight_claims, - PresentationVerificationType::Success, - ); - - let res: VerifyResult = from_json( - app.execute_contract( - second_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(second_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::PresentationExpired(exp) - ); -} - -#[test] -fn verify_route_not_registered() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Make a presentation with some claims - let claims = claims("Alice", 30, true, 2021, None); - let presentation = make_presentation(claims, PresentationVerificationType::Success); - - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - // Try verify presentation with not registered route - let err = app - .execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::RouteNotRegistered - )); -} - -#[test] -fn verify_success_validate_fails() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Make a presentation with some claims that does not match presentation requirements - let claims = claims("Alice", 30, true, 2014, None); - let presentation = make_presentation(claims, PresentationVerificationType::Success); - - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - let res: VerifyResult = from_json( - app.execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: FIRST_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::CriterionValueFailed("joined_at".to_string()) - ); -} - -#[test] -fn verify_required_claims_not_satisfied() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - let claims = claims("Alice", 30, true, 2021, None); - let presentation = make_presentation(claims, PresentationVerificationType::OmitAgeDisclosure); - - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - let res: VerifyResult = from_json( - app.execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: FIRST_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::DisclosedClaimNotFound( - "Expects claim to be: Number(NumberCriterion { value: 30, operator: EqualTo }) for attr: age".to_string() - ) - ); -} - -#[test] -fn verify_without_sdjwt() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - // Try verify presentation without sdjwt - let res: VerifyResult = from_json( - app.execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(b""), - route_id: FIRST_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::SdJwt("invalid input: Invalid SD-JWT length: 1".to_string()) - ); -} - -#[test] -fn verify_presentation_too_large() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Make a presentation with a too large claims - let claims = claims( - &"Very long name".repeat(MAX_PRESENTATION_LEN), - 30, - true, - 2021, - None, - ); - - let presentation = make_presentation(claims, PresentationVerificationType::Success); - let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); - - // Try verify too large presentation - let res: VerifyResult = from_json( - app.execute_contract( - first_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: FIRST_ROUTE_ID, - app_addr: Some(first_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert_eq!( - res.result.unwrap_err(), - SdjwtVerifierResultError::PresentationTooLarge - ); -} - -#[test] -fn verify_success_on_no_expiration_check_for_expired_claims() { - let mut app = App::default(); - - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get route verification requirements for a single route with expiration - let route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - RouteVerificationRequirementsType::Supported, - ); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - //Register the app with exp requirements - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: vec![RegisterRouteRequest { - route_id: SECOND_ROUTE_ID, - requirements: route_verification_req, - }], - }, - &[], - ) - .unwrap(); - - // Make a presentation with some claims that has expired - let exp = cw_utils::Expiration::AtTime(get_default_block_info().time.minus_days(1)); - let invalid_timestamp_claims = claims("Alice", 30, true, 2021, Some(exp)); - - let presentation = make_presentation( - invalid_timestamp_claims, - PresentationVerificationType::Success, - ); - - let res: VerifyResult = from_json( - app.execute_contract( - second_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Verify { - presentation: Binary::from(presentation.as_bytes()), - route_id: SECOND_ROUTE_ID, - app_addr: Some(second_caller_app_addr.to_string()), - additional_requirements: None, - }, - &[], - ) - .unwrap() - .data - .unwrap(), - ) - .unwrap(); - - assert!(res.result.is_ok()); -} - -#[test] -fn register_success() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req.clone(), - }, - &[], - ) - .unwrap(); - - // Ensure that app is registered with the expected routes and requirements - let registered_routes: Vec = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetRoutes { - app_addr: second_caller_app_addr.to_string(), - }, - ) - .unwrap(); - - assert_eq!(registered_routes.len(), 2); - - let second_registered_req: RouteVerificationRequirements = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetRouteRequirements { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - }, - ) - .unwrap(); - - assert_eq!( - second_registered_req.issuer_source_or_data, - two_routes_verification_req[0] - .requirements - .issuer_source_or_data - ); - - assert_eq!( - second_registered_req.presentation_required, - two_routes_verification_req[0] - .requirements - .presentation_required - ); - - let route_verification_key: Option = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetRouteVerificationKey { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - }, - ) - .unwrap(); - - let route_verification_jwk: josekit::jwk::Jwk = - serde_json::from_str(&route_verification_key.unwrap()).unwrap(); - - assert_eq!(route_verification_jwk, issuer_jwk()); - - let third_registered_req: RouteVerificationRequirements = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetRouteRequirements { - app_addr: second_caller_app_addr.to_string(), - route_id: THIRD_ROUTE_ID, - }, - ) - .unwrap(); - - assert_eq!( - third_registered_req.issuer_source_or_data, - two_routes_verification_req[1] - .requirements - .issuer_source_or_data - ); - - assert_eq!( - third_registered_req.presentation_required, - two_routes_verification_req[1] - .requirements - .presentation_required - ); - - let route_verification_key: Option = app - .wrap() - .query_wasm_smart( - contract_addr, - &QueryMsg::GetRouteVerificationKey { - app_addr: second_caller_app_addr.to_string(), - route_id: THIRD_ROUTE_ID, - }, - ) - .unwrap(); - - let route_verification_jwk: josekit::jwk::Jwk = - serde_json::from_str(&route_verification_key.unwrap()).unwrap(); + serde_json::from_str(&route_verification_keys.unwrap()[0]).unwrap(); assert_eq!(route_verification_jwk, issuer_jwk()); } - -#[test] -fn register_app_is_already_registered() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - // Register the app with the two routes - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req.clone(), - }, - &[], - ) - .unwrap(); - - // Try register the app with the two routes again - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::AppAlreadyRegistered - )); -} - -#[test] -fn register_serde_json_error() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let mut two_routes_verification_req = get_two_input_routes_requirements(); - - // Make invalid presentation request - two_routes_verification_req[0] - .requirements - .presentation_required = Some(Binary::from(b"invalid")); - - let owner = app.api().addr_make(OWNER_ADDR); - - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Try register the app with invalid presentation request - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::Std(_) - )); -} - -#[test] -fn register_unsupported_key_type() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get an unsupported input verification requirements for a single route - let unsupported_key_type_route_verification_requirement = - get_input_route_requirement(RouteVerificationRequirementsType::UnsupportedKeyType); - - let owner = app.api().addr_make(OWNER_ADDR); - - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Try register the app with the unsupported key type - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: vec![unsupported_key_type_route_verification_requirement], - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::UnsupportedKeyType - )); -} - -#[test] -fn deregister_success() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap(); - - // Deregister the app - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Deregister { - app_addr: second_caller_app_addr.to_string(), - }, - &[], - ) - .unwrap(); - - // Ensure there is no routes left after the app deregistration - let err = app - .wrap() - .query_wasm_smart::>( - contract_addr, - &QueryMsg::GetRoutes { - app_addr: second_caller_app_addr.to_string(), - }, - ) - .unwrap_err(); - - assert!(err.to_string().contains("not found")); -} - -#[test] -fn deregister_app_not_registered() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - let owner = app.api().addr_make(OWNER_ADDR); - - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Try deregister the not registered app - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Deregister { - app_addr: second_caller_app_addr.to_string(), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::AppIsNotRegistered - )); -} - -#[test] -fn deregister_unauthorized() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap(); - - // Try deregister the app using unauthorized caller address - let err = app - .execute_contract( - second_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Deregister { - app_addr: second_caller_app_addr.to_string(), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::Unauthorised - )); -} - -#[test] -fn update_success() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - let owner = app.api().addr_make(OWNER_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap(); - - // Get route verification requirements for a single route - let updated_route_verification_req = get_route_verification_requirement( - ExpirationCheck::Expires, - RouteVerificationRequirementsType::Supported, - ); - - // Update the route verification requirements - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - route_criteria: Some(updated_route_verification_req.clone()), - }, - &[], - ) - .unwrap(); - - // Ensure that the route verification requirements are updated - let updated_registered_req: RouteVerificationRequirements = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetRouteRequirements { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - }, - ) - .unwrap(); - - assert_eq!( - updated_registered_req.issuer_source_or_data, - updated_route_verification_req.issuer_source_or_data - ); - - assert_eq!( - updated_registered_req.presentation_required, - updated_route_verification_req.presentation_required - ); - - // Remove route requirements - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - route_criteria: None, - }, - &[], - ) - .unwrap(); - - // Verify route requirements are removed - assert!(app - .wrap() - .query_wasm_smart::( - contract_addr, - &QueryMsg::GetRouteRequirements { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - }, - ) - .is_err()); -} - -#[test] -fn update_app_not_registered() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get route verification requirements for a single route - let updated_route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - RouteVerificationRequirementsType::Supported, - ); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Try update the route verification requirements of the not registered app - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - route_criteria: Some(updated_route_verification_req), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::AppIsNotRegistered - )); -} - -#[test] -fn update_unauthorized() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner, - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap(); - - // Get route verification requirements for a single route - let updated_route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - RouteVerificationRequirementsType::Supported, - ); - - // Update the route verification requirements using unauthorized caller address - let err = app - .execute_contract( - second_caller_app_addr.clone(), - contract_addr, - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - route_criteria: Some(updated_route_verification_req), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::Unauthorised - )); -} - -#[test] -fn update_serde_json_error() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get input verification requirements for 2 routes - let two_routes_verification_req = get_two_input_routes_requirements(); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: two_routes_verification_req, - }, - &[], - ) - .unwrap(); - - // Get route verification requirements for a single route - let mut updated_route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - RouteVerificationRequirementsType::Supported, - ); - - // Try update the route verification requirements with invalid presentation request - updated_route_verification_req.presentation_required = Some(Binary::from(b"invalid")); - - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: SECOND_ROUTE_ID, - route_criteria: Some(updated_route_verification_req), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::Std(_) - )); -} - -#[test] -fn update_unsupported_key_type() { - let mut app = App::default(); - - // Instantiate verifier contract with some predefined parameters - let (contract_addr, _) = - instantiate_verifier_contract(&mut app, RouteVerificationRequirementsType::Supported); - - // Get route verification requirements for a single route - let route_verification_req = get_route_verification_requirement( - ExpirationCheck::NoExpiry, - RouteVerificationRequirementsType::Supported, - ); - - let owner = app.api().addr_make(OWNER_ADDR); - let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); - - // Register the app with the two routes - app.execute_contract( - owner.clone(), - contract_addr.clone(), - &AvidaVerifierExecuteMsg::Register { - app_addr: second_caller_app_addr.to_string(), - requests: vec![RegisterRouteRequest { - route_id: SECOND_ROUTE_ID, - requirements: route_verification_req, - }], - }, - &[], - ) - .unwrap(); - - // Get an unsupported input verification requirements for a single route - let unsupported_key_type_route_verification_requirement = - get_input_route_requirement(RouteVerificationRequirementsType::UnsupportedKeyType); - - // Try update the route verification requirements with unsupported key type - let err = app - .execute_contract( - owner, - contract_addr, - &AvidaVerifierExecuteMsg::Update { - app_addr: second_caller_app_addr.to_string(), - route_id: unsupported_key_type_route_verification_requirement.route_id, - route_criteria: Some( - unsupported_key_type_route_verification_requirement.requirements, - ), - }, - &[], - ) - .unwrap_err(); - - assert!(matches!( - err.downcast().unwrap(), - SdjwtVerifierError::UnsupportedKeyType - )); -} diff --git a/contracts/sdjwt-verifier/src/tests/verifier_update_test.rs b/contracts/sdjwt-verifier/src/tests/verifier_update_test.rs new file mode 100644 index 0000000..32c0e21 --- /dev/null +++ b/contracts/sdjwt-verifier/src/tests/verifier_update_test.rs @@ -0,0 +1,402 @@ +use cosmwasm_std::{to_json_binary, Binary}; +use cw_multi_test::{App, Executor}; + +use crate::{ + errors::SdjwtVerifierError, + types::{Criterion, JwkInfo, ReqAttr, CW_EXPIRATION}, +}; +use avida_common::types::{ + AvidaVerifierExecuteMsg, IssuerSourceOrData, RegisterRouteRequest, + RouteVerificationRequirements, +}; +use serde::{Deserialize, Serialize}; + +use super::fixtures::default_instantiate_verifier_contract; +use crate::msg::QueryMsg; +use avida_test_utils::sdjwt::fixtures::{ + get_default_presentation_required, get_input_route_requirement, + get_two_input_routes_requirements, issuer_jwk, make_route_verification_requirements, + ExpirationCheck, KeyType, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, OWNER_ADDR, + SECOND_CALLER_APP_ADDR, SECOND_ROUTE_ID, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub name: String, + pub age: u32, +} + +#[test] +fn update_adding_new_jwk() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + let app_admin = app.api().addr_make(FIRST_CALLER_APP_ADDR); + let default_issuer = "issuer"; + let new_jwk_issuer = "new-jwk-issuer"; + + // Get existing route pubkeys + let existing_registered_req = app + .wrap() + .query_wasm_smart::( + contract_addr.clone(), + &QueryMsg::GetRouteRequirements { + app_addr: app_admin.to_string(), + route_id: FIRST_ROUTE_ID, + }, + ) + .unwrap(); + + let pks = existing_registered_req.issuer_pubkeys.unwrap(); + assert!(pks.len() == 1); + assert!(pks.contains_key("issuer")); + let jwk = pks.get("issuer").unwrap(); + let data_or_location = serde_json::to_string(&jwk).unwrap(); + // Transform the first one back into the right format + let existing_jwk_info = JwkInfo { + jwk: Binary::from(data_or_location.as_bytes()), + issuer: default_issuer.to_string(), + }; + + // Create the second pubkey + let data_or_location = serde_json::to_string(&issuer_jwk()).unwrap(); + let new_jwk_info = JwkInfo { + jwk: Binary::from(data_or_location.as_bytes()), + issuer: new_jwk_issuer.to_string(), + }; + + let rvr = RouteVerificationRequirements { + issuer_source_or_data: vec![ + IssuerSourceOrData { + source: None, + data_or_location: to_json_binary(&new_jwk_info).unwrap(), + }, + IssuerSourceOrData { + source: None, + data_or_location: to_json_binary(&existing_jwk_info).unwrap(), + }, + ], + presentation_required: Some(Binary::from( + serde_json::to_string(&existing_registered_req.presentation_required) + .unwrap() + .as_bytes(), + )), + }; + + // Update the route verification requirements + app.execute_contract( + app_admin.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Update { + app_addr: app_admin.to_string(), + route_id: FIRST_ROUTE_ID, + route_criteria: Some(rvr), + }, + &[], + ) + .unwrap(); + + // Ensure that the route verification requirements are updated + let updated_registered_req = app + .wrap() + .query_wasm_smart::( + contract_addr.clone(), + &QueryMsg::GetRouteRequirements { + app_addr: app_admin.to_string(), + route_id: FIRST_ROUTE_ID, + }, + ) + .unwrap(); + + let pks = updated_registered_req.issuer_pubkeys.unwrap(); + assert!(pks.len() == 2); + assert!(pks.contains_key("issuer")); + assert!(pks.contains_key("new-jwk-issuer")); +} + +#[test] +fn update_app_not_registered() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get route verification requirements for a single route + let updated_req = get_default_presentation_required(ExpirationCheck::NoExpiry); + let updated_route_verification_req = + make_route_verification_requirements(updated_req, KeyType::Ed25519); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Try update the route verification requirements of the not registered app + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + route_criteria: Some(updated_route_verification_req), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::AppIsNotRegistered + )); +} + +#[test] +fn update_unauthorized() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap(); + + // Get route verification requirements for a single route + let updated_req = get_default_presentation_required(ExpirationCheck::NoExpiry); + let updated_route_verification_req = + make_route_verification_requirements(updated_req, KeyType::Ed25519); + + // Update the route verification requirements using unauthorized caller address + let err = app + .execute_contract( + second_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + route_criteria: Some(updated_route_verification_req), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::Unauthorised + )); +} + +#[test] +fn update_serde_json_error() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req, + }, + &[], + ) + .unwrap(); + + // Get route verification requirements for a single route + let req = get_default_presentation_required(ExpirationCheck::Expires); + let mut updated_route_verification_req = + make_route_verification_requirements(req, KeyType::RSA); + + // Try update the route verification requirements with invalid presentation request + updated_route_verification_req.presentation_required = Some(Binary::from(b"invalid")); + + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + route_criteria: Some(updated_route_verification_req), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::Std(_) + )); +} + +#[test] +fn update_unsupported_key_type() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get route verification requirements for a single route + let req = get_default_presentation_required(ExpirationCheck::Expires); + let route_verification_req = make_route_verification_requirements(req, KeyType::Ed25519); + + let owner = app.api().addr_make(OWNER_ADDR); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: vec![RegisterRouteRequest { + route_id: SECOND_ROUTE_ID, + requirements: route_verification_req, + }], + }, + &[], + ) + .unwrap(); + + // Get an unsupported input verification requirements for a single route + let unsupported_key_type_route_verification_requirement = + get_input_route_requirement(KeyType::RSA); + + // Try update the route verification requirements with unsupported key type + let err = app + .execute_contract( + owner, + contract_addr, + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: unsupported_key_type_route_verification_requirement.route_id, + route_criteria: Some( + unsupported_key_type_route_verification_requirement.requirements, + ), + }, + &[], + ) + .unwrap_err(); + + assert!(matches!( + err.downcast().unwrap(), + SdjwtVerifierError::UnsupportedKeyType + )); +} + +#[test] +fn update_adding_and_remove_extra_route() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Get input verification requirements for 2 routes + let two_routes_verification_req = get_two_input_routes_requirements(); + let second_caller_app_addr = app.api().addr_make(SECOND_CALLER_APP_ADDR); + + let owner = app.api().addr_make(OWNER_ADDR); + + // Register the app with the two routes + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Register { + app_addr: second_caller_app_addr.to_string(), + requests: two_routes_verification_req.clone(), + }, + &[], + ) + .unwrap(); + + // Get route verification requirements for a single route + let updated_req = get_default_presentation_required(ExpirationCheck::Expires); + let updated_route_verification_req = + make_route_verification_requirements(updated_req, KeyType::Ed25519); + + // Update the route verification requirements + app.execute_contract( + owner.clone(), + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + route_criteria: Some(updated_route_verification_req.clone()), + }, + &[], + ) + .unwrap(); + + // Ensure that the route verification requirements are updated + let updated_registered_req = app + .wrap() + .query_wasm_smart::( + contract_addr.clone(), + &QueryMsg::GetRouteRequirements { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + }, + ) + .unwrap(); + + let exp = updated_registered_req + .presentation_required + .iter() + .find(|attr| { + **attr + == ReqAttr { + attribute: CW_EXPIRATION.to_string(), + criterion: Criterion::Expires(true), + } + }); + + assert!(exp.is_some()); + + // Remove route requirements + app.execute_contract( + owner, + contract_addr.clone(), + &AvidaVerifierExecuteMsg::Update { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + route_criteria: None, + }, + &[], + ) + .unwrap(); + + // Verify route requirements are removed + assert!(app + .wrap() + .query_wasm_smart::( + contract_addr, + &QueryMsg::GetRouteRequirements { + app_addr: second_caller_app_addr.to_string(), + route_id: SECOND_ROUTE_ID, + }, + ) + .is_err()); +} diff --git a/contracts/sdjwt-verifier/src/tests/verifier_verify_test.rs b/contracts/sdjwt-verifier/src/tests/verifier_verify_test.rs new file mode 100644 index 0000000..352361a --- /dev/null +++ b/contracts/sdjwt-verifier/src/tests/verifier_verify_test.rs @@ -0,0 +1,178 @@ +use cosmwasm_std::{from_json, Binary}; +use cw_multi_test::{App, Executor}; + +use crate::errors::SdjwtVerifierResultError; +use crate::types::VerifyResult; +use serde::{Deserialize, Serialize}; + +use super::fixtures::default_instantiate_verifier_contract; +use avida_common::types::AvidaVerifierExecuteMsg; +use avida_test_utils::sdjwt::fixtures::{ + claims, make_presentation, PresentationVerificationType, FIRST_CALLER_APP_ADDR, FIRST_ROUTE_ID, + MAX_PRESENTATION_LEN, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub name: String, + pub age: u32, +} + +#[test] +fn verify_success_incorrect_claims_validate_fails() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Make a presentation with some claims that does not match presentation requirements + // Criteria (age == 30, active == true, joined_at > 2020) + // Criterion joined_at > 2020 will fail + let claims = claims("Alice", 30, true, 2014, None); + let presentation = make_presentation(claims, PresentationVerificationType::Success); + + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + let res: VerifyResult = from_json( + app.execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: FIRST_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::CriterionValueFailed("joined_at".to_string()) + ); +} + +#[test] +fn verify_required_claims_not_satisfied() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + let claims = claims("Alice", 30, true, 2021, None); + // We omit age disclosure to fail the criteria + // Criteria (age == 30, active == true, joined_at > 2020) + let presentation = make_presentation(claims, PresentationVerificationType::OmitAgeDisclosure); + + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + let res: VerifyResult = from_json( + app.execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: FIRST_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::DisclosedClaimNotFound( + "Expects claim to be: Number(NumberCriterion { value: 30, operator: EqualTo }) for attr: age".to_string() + ) + ); +} + +#[test] +fn verify_without_sdjwt() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + // Try verify presentation without sdjwt + let res: VerifyResult = from_json( + app.execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(b""), + route_id: FIRST_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::SdJwtRsError( + "invalid input: Invalid SD-JWT length: 1".to_string() + ) + ); +} + +#[test] +fn verify_presentation_too_large() { + let mut app = App::default(); + + // Instantiate verifier contract with some predefined parameters + let (contract_addr, _) = default_instantiate_verifier_contract(&mut app); + + // Make a presentation with a too large claims + let claims = claims( + &"Very long name".repeat(MAX_PRESENTATION_LEN), + 30, + true, + 2021, + None, + ); + + let presentation = make_presentation(claims, PresentationVerificationType::Success); + let first_caller_app_addr = app.api().addr_make(FIRST_CALLER_APP_ADDR); + + // Try verify too large presentation + let res: VerifyResult = from_json( + app.execute_contract( + first_caller_app_addr.clone(), + contract_addr, + &AvidaVerifierExecuteMsg::Verify { + presentation: Binary::from(presentation.as_bytes()), + route_id: FIRST_ROUTE_ID, + app_addr: Some(first_caller_app_addr.to_string()), + additional_requirements: None, + }, + &[], + ) + .unwrap() + .data + .unwrap(), + ) + .unwrap(); + + assert_eq!( + res.result.unwrap_err(), + SdjwtVerifierResultError::PresentationTooLarge + ); +} diff --git a/contracts/sdjwt-verifier/src/types.rs b/contracts/sdjwt-verifier/src/types.rs index f652c24..31fd70a 100644 --- a/contracts/sdjwt-verifier/src/types.rs +++ b/contracts/sdjwt-verifier/src/types.rs @@ -1,5 +1,4 @@ use super::errors::{SdjwtVerifierError, SdjwtVerifierResultError}; - use avida_common::types::RegisterRouteRequest; use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_json, Binary, BlockInfo, SubMsg}; @@ -7,6 +6,7 @@ use cw_utils::Expiration; use jsonwebtoken::jwk::Jwk; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashMap; /// This is the key to be used in claims that specifies expiration using `cw_util::Expiration` pub const CW_EXPIRATION: &str = "cw_exp"; @@ -14,6 +14,15 @@ pub const CW_EXPIRATION: &str = "cw_exp"; /// This is the key to be used in revocation_list in Criterion::NotContainedIn(revocation_list) pub const IDX: &str = "idx"; +// This is the key to be used in claims that specifies the issuer of the JWT +pub const ISS_KEY: &str = "iss"; + +#[cw_serde] +pub struct JwkInfo { + pub jwk: Binary, + pub issuer: String, +} + #[cw_serde] pub struct VerifyResult { pub result: Result, @@ -37,18 +46,18 @@ pub struct PendingRoute { /// 2. if issuer data is directly provided, it will validate the jwk pub(crate) struct _RegistrationRequest { pub verification_requirements: VerificationRequirements, - pub ibc_msg: Option, + pub ibc_msgs: Option>, } impl _RegistrationRequest { /// Create a new verification request pub fn new( verification_requirements: VerificationRequirements, - ibc_msg: Option, + ibc_msgs: Option>, ) -> Self { _RegistrationRequest { verification_requirements, - ibc_msg, + ibc_msgs, } } } @@ -79,20 +88,21 @@ pub struct VerificationRequirements { /// it is sent from presentation_request in the `RouteVerificationRequirements` pub presentation_required: PresentationReq, /// Usig this type as it is ser/deserializable - pub issuer_pubkey: Option, + // use the `iss` value here as the key of the map + pub issuer_pubkeys: Option>, } impl VerificationRequirements { pub fn new( presentation_request: Option, - issuer_pubkey: Option, + issuer_pubkeys: Option>, ) -> Result { Ok(VerificationRequirements { presentation_required: match presentation_request { Some(binary) => from_json(&binary)?, None => vec![], }, - issuer_pubkey, + issuer_pubkeys, }) } } diff --git a/contracts/sdjwt-verifier/src/verifier.rs b/contracts/sdjwt-verifier/src/verifier.rs index af304e5..8071110 100644 --- a/contracts/sdjwt-verifier/src/verifier.rs +++ b/contracts/sdjwt-verifier/src/verifier.rs @@ -1,11 +1,9 @@ -use std::collections::HashMap; - use crate::{ errors::{SdjwtVerifierError, SdjwtVerifierResultError}, state::*, types::{ - validate, Criterion, PendingRoute, PresentationReq, VerificationRequirements, VerifyResult, - _RegistrationRequest, IDX, + validate, Criterion, JwkInfo, PendingRoute, PresentationReq, VerificationRequirements, + VerifyResult, _RegistrationRequest, IDX, ISS_KEY, }, }; use avida_cheqd::{ @@ -13,9 +11,10 @@ use avida_cheqd::{ types::ResourceReqPacket, }; use avida_common::types::{ - IssuerSourceOrData, RegisterRouteRequest, RouteId, RouteVerificationRequirements, - TrustRegistry, VerfiablePresentation, MAX_PRESENTATION_LENGTH, + RegisterRouteRequest, RouteId, RouteVerificationRequirements, TrustRegistry, + VerfiablePresentation, MAX_PRESENTATION_LENGTH, }; +use cosmwasm_std::Order; use cosmwasm_std::{ ensure, from_json, to_json_binary, Addr, Binary, BlockInfo, CosmosMsg, Deps, DepsMut, Env, IbcBasicResponse, IbcChannelConnectMsg, IbcPacketAckMsg, IbcTimeout, MessageInfo, Response, @@ -23,16 +22,19 @@ use cosmwasm_std::{ }; use sd_jwt_rs::{SDJWTSerializationFormat, SDJWTVerifier}; use serde_json::Value; +use std::collections::HashMap; use avida_common::types::UpdateRevocationListRequest; use jsonwebtoken::{ jwk::{AlgorithmParameters, EllipticCurve, Jwk, OctetKeyPairParameters}, DecodingKey, }; +use sd_jwt_rs::SDJWTCommon; // Execute message handlers pub fn handle_update_revocation_list( deps: DepsMut, + info: MessageInfo, app_addr: String, request: UpdateRevocationListRequest, ) -> Result { @@ -42,12 +44,18 @@ pub fn handle_update_revocation_list( unrevoke, } = request; - let mut all_routes_requirements = APP_ROUTES_REQUIREMENTS.load(deps.storage, &app_addr)?; + let mut route_requirements = + APP_ROUTES_REQUIREMENTS.load(deps.storage, (app_addr.clone(), route_id))?; - let mut route_requirements = all_routes_requirements - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)? - .clone(); + let valid_app_addr = deps.api.addr_validate(&app_addr)?; + + let app_admin = APP_ADMINS + .load(deps.storage, valid_app_addr.as_str()) + .map_err(|_| SdjwtVerifierError::AppIsNotRegistered)?; + + if app_admin != info.sender { + return Err(SdjwtVerifierError::Unauthorised); + } route_requirements .presentation_required @@ -71,9 +79,7 @@ pub fn handle_update_revocation_list( }) .ok_or(SdjwtVerifierError::IDXNotInRequirement)??; - all_routes_requirements.insert(route_id, route_requirements); - - APP_ROUTES_REQUIREMENTS.save(deps.storage, &app_addr, &all_routes_requirements)?; + APP_ROUTES_REQUIREMENTS.save(deps.storage, (app_addr, route_id), &route_requirements)?; Ok(Response::default()) } @@ -107,13 +113,11 @@ pub fn handle_verify( let additional_requirements: Option = additional_requirements.map(from_json).transpose()?; let app_addr = app_addr.unwrap_or_else(|| info.sender.to_string()); - let app_addr = deps.api.addr_validate(&app_addr)?; let requirements = APP_ROUTES_REQUIREMENTS - .load(deps.storage, app_addr.as_str())? - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)? - .clone(); + .load(deps.storage, (app_addr, route_id)) + .map_err(|_| SdjwtVerifierError::RouteNotRegistered)?; + let max_len = MAX_PRESENTATION_LENGTH.load(deps.storage)?; let res = _verify( @@ -160,8 +164,11 @@ pub fn handle_deregister( info: MessageInfo, app_addr: String, ) -> Result { - if !APP_TRUST_DATA_SOURCE.has(deps.storage, &app_addr) - || !APP_ROUTES_REQUIREMENTS.has(deps.storage, &app_addr) + if APP_ROUTES_REQUIREMENTS + .prefix(app_addr.clone()) + .range(deps.storage, None, None, Order::Ascending) + .next() + .is_none() { return Err(SdjwtVerifierError::AppIsNotRegistered); } @@ -189,9 +196,8 @@ pub fn handle_sudo_verify( additional_requirements.map(from_json).transpose()?; let requirements = APP_ROUTES_REQUIREMENTS - .load(deps.storage, &app_addr)? - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)? + .load(deps.storage, (app_addr, route_id)) + .map_err(|_| SdjwtVerifierError::RouteNotRegistered)? .clone(); let max_len = MAX_PRESENTATION_LENGTH.load(deps.storage)?; @@ -219,19 +225,22 @@ pub fn handle_sudo_update( } // Query handlers -pub fn query_route_verification_key( +pub fn query_route_verification_keys( deps: Deps, app_addr: String, route_id: RouteId, -) -> Result, SdjwtVerifierError> { - let req = APP_ROUTES_REQUIREMENTS.load(deps.storage, &app_addr)?; - let route_req = req - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)?; - Ok(route_req - .issuer_pubkey - .as_ref() - .map(|jwk| serde_json::to_string(jwk).unwrap())) +) -> Result>, SdjwtVerifierError> { + let route_req = APP_ROUTES_REQUIREMENTS + .load(deps.storage, (app_addr, route_id)) + .map_err(|_| SdjwtVerifierError::RouteNotRegistered)?; + + let keys = route_req.issuer_pubkeys.as_ref().map(|jwks| { + jwks.iter() + .map(|(_, jwk)| serde_json::to_string(jwk).unwrap()) + .collect() + }); + + Ok(keys) } pub fn query_app_admin(deps: Deps, app_addr: String) -> Result { @@ -240,8 +249,10 @@ pub fn query_app_admin(deps: Deps, app_addr: String) -> Result Result, SdjwtVerifierError> { - let v = APP_ROUTES_REQUIREMENTS.load(deps.storage, &app_addr)?; - let routes: Vec = v.keys().cloned().collect(); + let routes: Vec = APP_ROUTES_REQUIREMENTS + .prefix(app_addr) + .keys(deps.storage, None, None, Order::Ascending) + .collect::, _>>()?; Ok(routes) } @@ -249,25 +260,11 @@ pub fn query_route_requirements( deps: Deps, app_addr: String, route_id: RouteId, -) -> Result { - let req = APP_ROUTES_REQUIREMENTS.load(deps.storage, &app_addr)?; - let route_req = req - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)?; - - let trust_data = APP_TRUST_DATA_SOURCE.load(deps.storage, &app_addr)?; - let route_td = trust_data - .get(&route_id) - .ok_or(SdjwtVerifierError::RouteNotRegistered)?; - - Ok(RouteVerificationRequirements { - issuer_source_or_data: route_td.clone(), - presentation_required: if route_req.presentation_required.is_empty() { - None - } else { - Some(to_json_binary(&route_req.presentation_required)?) - }, - }) +) -> Result { + let req = APP_ROUTES_REQUIREMENTS + .load(deps.storage, (app_addr, route_id)) + .map_err(|_| SdjwtVerifierError::RouteNotRegistered)?; + Ok(req) } /// Verify the provided presentation within the context of the given route @@ -284,40 +281,63 @@ pub fn _verify( SdjwtVerifierResultError::PresentationTooLarge ); - let decoding_key = DecodingKey::from_jwk( - requirements - .issuer_pubkey - .as_ref() - .ok_or(SdjwtVerifierResultError::PubKeyNotFound)?, - ) - .map_err(|e| SdjwtVerifierResultError::JwtError(e.to_string()))?; - - // We verify the presentation - let sdjwt_verifier = SDJWTVerifier::new( + let mut common = SDJWTCommon::default(); + common.parse_compact_sd_jwt( String::from_utf8(presentation.to_vec()) .map_err(|e| SdjwtVerifierResultError::StringConversion(e.to_string()))?, - Box::new(move |_, _| decoding_key.clone()), - None, // This version does not support key binding - None, // This version does not support key binding - SDJWTSerializationFormat::Compact, - ) - .map_err(|e| SdjwtVerifierResultError::SdJwt(e.to_string()))?; - - let combined_requirements = if let Some(additional_requirements) = additional_requirements { - let mut combined_requirements = requirements.presentation_required.clone(); - combined_requirements.extend(additional_requirements); - combined_requirements - } else { - requirements.presentation_required - }; - - // We validate the verified claims against the requirements - validate( - combined_requirements, - sdjwt_verifier.verified_claims.clone(), - block_info, )?; - Ok(sdjwt_verifier.verified_claims) + + let payload = common + .unverified_input_sd_jwt_payload + .ok_or(SdjwtVerifierResultError::IssuerNotFound)?; + + let iss = payload + .get(ISS_KEY) + .ok_or(SdjwtVerifierResultError::IssuerNotFound)? + .as_str() + .ok_or(SdjwtVerifierResultError::StringConversion( + "Iss is not a string".to_owned(), + ))?; + + if let Some(pubkeys) = requirements.issuer_pubkeys { + let decoding_key = if let Some(pubkey) = pubkeys.get(iss) { + DecodingKey::from_jwk(pubkey) + .map_err(|e| SdjwtVerifierResultError::JwtError(e.to_string()))? + } else { + return Err(SdjwtVerifierResultError::PubKeyNotFound); + }; + + // We verify the presentation + let sdjwt_verifier = SDJWTVerifier::new( + String::from_utf8(presentation.to_vec()) + .map_err(|e| SdjwtVerifierResultError::StringConversion(e.to_string()))?, + Box::new(move |_, _| decoding_key.clone()), + None, // This version does not support key binding + None, // This version does not support key binding + SDJWTSerializationFormat::Compact, + ) + .map_err(|e| SdjwtVerifierResultError::SdJwt(e.to_string()))?; + + let combined_requirements = if let Some(additional_requirements) = additional_requirements { + let mut combined_requirements = requirements.presentation_required.clone(); + combined_requirements.extend(additional_requirements); + combined_requirements + } else { + requirements.presentation_required + }; + + // We validate the verified claims against the requirements + validate( + combined_requirements, + sdjwt_verifier.verified_claims.clone(), + block_info, + )?; + Ok(sdjwt_verifier.verified_claims) + } + // If the issuer is not in the requirements, we return an error + else { + Err(SdjwtVerifierResultError::IssuerNotFound) + } } /// Performs a registration of an application and all its routes @@ -328,40 +348,42 @@ pub fn _register( app_addr: &str, route_criteria: Vec, ) -> Result { - if APP_TRUST_DATA_SOURCE.has(storage, app_addr) - || APP_ROUTES_REQUIREMENTS.has(storage, app_addr) + if APP_ROUTES_REQUIREMENTS + .prefix(app_addr.to_owned()) + .range(storage, None, None, Order::Ascending) + .next() + .is_some() { return Err(SdjwtVerifierError::AppAlreadyRegistered); } - let mut req_map: HashMap = HashMap::new(); - let mut data_sources: HashMap = HashMap::new(); - let mut response = Response::default(); for RegisterRouteRequest { route_id, requirements, } in route_criteria + // here we get the iss to insert into the APP_ROUTES_REQUIREMENTS { - data_sources.insert(route_id, requirements.issuer_source_or_data.clone()); // On registration we check if the dApp has request for IBC data // Make a verification request for specified app addr and route id with a provided route criteria let _RegistrationRequest { verification_requirements, - ibc_msg, + ibc_msgs, } = make_internal_registration_request(storage, env, app_addr, route_id, requirements)?; - req_map.insert(route_id, verification_requirements); + // Save the registered trust data sources and route requirements + APP_ROUTES_REQUIREMENTS.save( + storage, + (app_addr.to_owned(), route_id), + &verification_requirements, + )?; - if let Some(ibc_msg) = ibc_msg { - response = response.add_submessage(ibc_msg); + if let Some(ibc_msgs) = ibc_msgs { + response = response.add_submessages(ibc_msgs); } } - // Save the registered trust data sources and route requirements - APP_TRUST_DATA_SOURCE.save(storage, app_addr, &data_sources)?; - APP_ROUTES_REQUIREMENTS.save(storage, app_addr, &req_map)?; APP_ADMINS.save(storage, app_addr, admin)?; Ok(response) @@ -369,8 +391,9 @@ pub fn _register( /// Performs a deregister of an application and all its routes fn _deregister(storage: &mut dyn Storage, app_addr: &str) -> Result { - APP_TRUST_DATA_SOURCE.remove(storage, app_addr); - APP_ROUTES_REQUIREMENTS.remove(storage, app_addr); + APP_ROUTES_REQUIREMENTS + .prefix(app_addr.to_owned()) + .clear(storage, None); APP_ADMINS.remove(storage, app_addr); Ok(Response::default()) @@ -385,46 +408,49 @@ fn _update( route_criteria: Option, ) -> Result { // Ensure the app with this address is registered - if !APP_TRUST_DATA_SOURCE.has(storage, app_addr) - || !APP_ROUTES_REQUIREMENTS.has(storage, app_addr) + if APP_ROUTES_REQUIREMENTS + .prefix(app_addr.to_owned()) + .range(storage, None, None, Order::Ascending) + .next() + .is_none() { return Err(SdjwtVerifierError::AppIsNotRegistered); } - let mut req_map = APP_ROUTES_REQUIREMENTS.load(storage, app_addr)?; - let mut data_sources = APP_TRUST_DATA_SOURCE.load(storage, app_addr)?; - let mut response: Response = Response::default(); // On registration we check if the dApp has request for IBC data if let Some(route_criteria) = route_criteria { - data_sources.insert(route_id, route_criteria.issuer_source_or_data.clone()); - // Make a verification request for specified app addr and route id with a provided route criteria let _RegistrationRequest { verification_requirements, - ibc_msg, + ibc_msgs, } = make_internal_registration_request(storage, env, app_addr, route_id, route_criteria)?; - req_map.insert(route_id, verification_requirements); + APP_ROUTES_REQUIREMENTS.save( + storage, + (app_addr.to_owned(), route_id), + &verification_requirements, + )?; - if let Some(ibc_msg) = ibc_msg { - response = response.add_submessage(ibc_msg); + if let Some(ibc_msgs) = ibc_msgs { + response = response.add_submessages(ibc_msgs); } - } else { - data_sources.remove(&route_id); - req_map.remove(&route_id); - } - if data_sources.is_empty() && req_map.is_empty() { - // If there are no more routes, deregister the app - _deregister(storage, app_addr) + Ok(response) } else { - // Save the updated trust data sources and route requirements - APP_TRUST_DATA_SOURCE.save(storage, app_addr, &data_sources)?; - APP_ROUTES_REQUIREMENTS.save(storage, app_addr, &req_map)?; + APP_ROUTES_REQUIREMENTS.remove(storage, (app_addr.to_owned(), route_id)); - Ok(response) + if APP_ROUTES_REQUIREMENTS + .prefix(app_addr.to_owned()) + .range(storage, None, None, Order::Ascending) + .next() + .is_none() + { + _deregister(storage, app_addr) + } else { + Ok(response) + } } } @@ -436,53 +462,63 @@ fn make_internal_registration_request( route_id: RouteId, route_criteria: RouteVerificationRequirements, ) -> Result<_RegistrationRequest, SdjwtVerifierError> { - if let Some(registry) = route_criteria.issuer_source_or_data.source { - match registry { - TrustRegistry::Cheqd => { - // For Cheqd, the data is in the ResourceReqPacket - let resource_req_packat: ResourceReqPacket = - from_json(&route_criteria.issuer_source_or_data.data_or_location)?; - - let ibc_msg = SubMsg::new(CosmosMsg::Ibc(cosmwasm_std::IbcMsg::SendPacket { - channel_id: CHANNEL_ID.load(storage)?, - data: to_json_binary(&resource_req_packat)?, - timeout: IbcTimeout::with_timestamp(get_timeout_timestamp( - env, - HOUR_PACKET_LIFETIME, - )), - })); - - PENDING_VERIFICATION_REQ_REQUESTS.save( - storage, - &resource_req_packat.to_string(), - &PendingRoute { - app_addr: app_addr.to_string(), - route_id, - }, - )?; - - let verification_req = - VerificationRequirements::new(route_criteria.presentation_required, None)?; - Ok(_RegistrationRequest::new(verification_req, Some(ibc_msg))) + let mut ibc_submsgs: Vec = Vec::new(); + let mut issuer_pubkeys: HashMap = HashMap::new(); + + let mut vr = VerificationRequirements::new(route_criteria.presentation_required, None)?; + + for isd in route_criteria.issuer_source_or_data.iter() { + if let Some(registry) = &isd.source { + match registry { + // We query this data via IBC + TrustRegistry::Cheqd => { + // For Cheqd, the data is in the ResourceReqPacket + let resource_req_packat: ResourceReqPacket = from_json(&isd.data_or_location)?; + + let ibc_msg = SubMsg::new(CosmosMsg::Ibc(cosmwasm_std::IbcMsg::SendPacket { + channel_id: CHANNEL_ID.load(storage)?, + data: to_json_binary(&resource_req_packat)?, + timeout: IbcTimeout::with_timestamp(get_timeout_timestamp( + env, + HOUR_PACKET_LIFETIME, + )), + })); + + PENDING_VERIFICATION_REQ_REQUESTS.save( + storage, + &resource_req_packat.to_string(), + &PendingRoute { + app_addr: app_addr.to_string(), + route_id, + }, + )?; + + ibc_submsgs.push(ibc_msg); + } + } + } else { + let issuer_pubkey_info: JwkInfo = from_json(&isd.data_or_location)?; + let pubkey: Jwk = from_json(&issuer_pubkey_info.jwk)?; + + if let AlgorithmParameters::OctetKeyPair(OctetKeyPairParameters { + curve: EllipticCurve::Ed25519, + .. + }) = pubkey.algorithm + { + issuer_pubkeys.insert(issuer_pubkey_info.issuer, pubkey); + } else { + return Err(SdjwtVerifierError::UnsupportedKeyType); } } - } else { - let issuer_pubkey: Jwk = from_json(&route_criteria.issuer_source_or_data.data_or_location)?; + } - if let AlgorithmParameters::OctetKeyPair(OctetKeyPairParameters { - curve: EllipticCurve::Ed25519, - .. - }) = issuer_pubkey.algorithm - { - let verification_req = VerificationRequirements::new( - route_criteria.presentation_required, - Some(issuer_pubkey), - )?; + // Update vr with the latest issuer_pubkeys + vr.issuer_pubkeys = issuer_pubkeys.into(); - Ok(_RegistrationRequest::new(verification_req, None)) - } else { - Err(SdjwtVerifierError::UnsupportedKeyType) - } + if !ibc_submsgs.is_empty() { + Ok(_RegistrationRequest::new(vr, Some(ibc_submsgs))) + } else { + Ok(_RegistrationRequest::new(vr, None)) } } @@ -512,18 +548,23 @@ pub fn ibc_packet_ack_handler( PENDING_VERIFICATION_REQ_REQUESTS.remove(deps.storage, &resource_req_packet.to_string()); // Checks the return data is the expected format - let pubkey: Jwk = from_json(resource.linked_resource.data) + let pubkeys: HashMap = from_json(resource.linked_resource.data) .map_err(|e| SdjwtVerifierError::ReturnedResourceFormat(e.to_string()))?; - let mut req = APP_ROUTES_REQUIREMENTS.load(deps.storage, &pending_route.app_addr)?; + let mut req = APP_ROUTES_REQUIREMENTS + .load( + deps.storage, + (pending_route.app_addr.clone(), pending_route.route_id), + ) + .map_err(|_| SdjwtVerifierError::NoRequirementsForRoute)?; - let r = req - .get_mut(&pending_route.route_id) - .ok_or(SdjwtVerifierError::NoRequirementsForRoute)?; + req.issuer_pubkeys = Some(pubkeys); - r.issuer_pubkey = Some(pubkey); - - APP_ROUTES_REQUIREMENTS.save(deps.storage, &pending_route.app_addr, &req)?; + APP_ROUTES_REQUIREMENTS.save( + deps.storage, + (pending_route.app_addr, pending_route.route_id), + &req, + )?; Ok(IbcBasicResponse::new()) } diff --git a/packages/avida_test_utils/src/sdjwt/fixtures.rs b/packages/avida_test_utils/src/sdjwt/fixtures.rs index e8315da..1809282 100644 --- a/packages/avida_test_utils/src/sdjwt/fixtures.rs +++ b/packages/avida_test_utils/src/sdjwt/fixtures.rs @@ -1,6 +1,8 @@ +use avida_sdjwt_verifier::types::JwkInfo; use avida_sdjwt_verifier::types::NumberCriterion; use avida_sdjwt_verifier::types::ReqAttr; use avida_sdjwt_verifier::types::IDX; +use cosmwasm_std::to_json_binary; use cosmwasm_std::BlockInfo; use cw_utils::Expiration; use jsonwebtoken::EncodingKey; @@ -45,9 +47,9 @@ pub enum ExpirationCheck { } /// This is used to test different cases for route verification requirements -pub enum RouteVerificationRequirementsType { - Supported, - UnsupportedKeyType, +pub enum KeyType { + Ed25519, + RSA, } /// This is used to test different cases for presentation verification @@ -164,24 +166,25 @@ pub fn make_presentation( /// Is used to get route verification requirements pub fn make_route_verification_requirements( presentation_req: PresentationReq, - route_verification_requirements_type: RouteVerificationRequirementsType, + keytype: KeyType, ) -> RouteVerificationRequirements { let re = serde_json::to_string(&presentation_req).unwrap(); - let data_or_location = match route_verification_requirements_type { - RouteVerificationRequirementsType::Supported => { - serde_json::to_string(&issuer_jwk()).unwrap() - } - RouteVerificationRequirementsType::UnsupportedKeyType => { - serde_json::to_string(&rsa_issuer_jwk()).unwrap() - } + let data_or_location = match keytype { + KeyType::Ed25519 => serde_json::to_string(&issuer_jwk()).unwrap(), + KeyType::RSA => serde_json::to_string(&rsa_issuer_jwk()).unwrap(), + }; + + let jwk_info = JwkInfo { + jwk: Binary::from(data_or_location.as_bytes()), + issuer: "issuer".to_string(), }; // Add some default criteria as presentation request RouteVerificationRequirements { - issuer_source_or_data: IssuerSourceOrData { + issuer_source_or_data: vec![IssuerSourceOrData { source: None, - data_or_location: Binary::from(data_or_location.as_bytes()), - }, + data_or_location: to_json_binary(&jwk_info).unwrap(), + }], presentation_required: Some(Binary::from(re.as_bytes())), } } @@ -196,7 +199,7 @@ pub fn get_route_requirement_with_empty_revocation_list(route_id: u64) -> Regist route_id, requirements: make_route_verification_requirements( first_presentation_req, - RouteVerificationRequirementsType::Supported, + KeyType::Ed25519, ), } } @@ -239,32 +242,26 @@ pub fn get_two_input_routes_requirements() -> Vec { }, ]; - let pretty_json = serde_json::to_string(&first_presentation_req).unwrap(); - println!("reg {:?}", pretty_json); - vec![ RegisterRouteRequest { route_id: SECOND_ROUTE_ID, requirements: make_route_verification_requirements( first_presentation_req, - RouteVerificationRequirementsType::Supported, + KeyType::Ed25519, ), }, RegisterRouteRequest { route_id: THIRD_ROUTE_ID, requirements: make_route_verification_requirements( second_presentation_req, - RouteVerificationRequirementsType::Supported, + KeyType::Ed25519, ), }, ] } /// Is used to get route verification requirements for a single route -pub fn get_route_verification_requirement( - expiration_check: ExpirationCheck, - route_verification_requirements_type: RouteVerificationRequirementsType, -) -> RouteVerificationRequirements { +pub fn get_default_presentation_required(expiration_check: ExpirationCheck) -> PresentationReq { let mut presentation_req: PresentationReq = vec![ ReqAttr { attribute: "age".to_string(), @@ -292,13 +289,11 @@ pub fn get_route_verification_requirement( )) }; - make_route_verification_requirements(presentation_req, route_verification_requirements_type) + presentation_req } /// Is used to get route verification requirements for a single route -pub fn get_input_route_requirement( - route_verification_requirements_type: RouteVerificationRequirementsType, -) -> RegisterRouteRequest { +pub fn get_input_route_requirement(key_type: KeyType) -> RegisterRouteRequest { let presentation_req: PresentationReq = vec![ ReqAttr { attribute: "age".to_string(), @@ -321,10 +316,7 @@ pub fn get_input_route_requirement( ]; RegisterRouteRequest { route_id: SECOND_ROUTE_ID, - requirements: make_route_verification_requirements( - presentation_req, - route_verification_requirements_type, - ), + requirements: make_route_verification_requirements(presentation_req, key_type), } } diff --git a/packages/common/src/types.rs b/packages/common/src/types.rs index 96d3b88..1555adc 100644 --- a/packages/common/src/types.rs +++ b/packages/common/src/types.rs @@ -27,7 +27,7 @@ pub struct UpdateRevocationListRequest { #[cw_serde] pub struct RouteVerificationRequirements { /// This defines where the source data for verification is - pub issuer_source_or_data: IssuerSourceOrData, + pub issuer_source_or_data: Vec, /// The presentation request is the criteria required for the presentation, /// for example required certains claims to be disclosed /// This value is stored as `VerificationRequirements.presentation_required` on sdjwtVerifier @@ -47,7 +47,7 @@ pub struct IssuerSourceOrData { /// The data or location of the verification data at the trust registry /// For TrustRegistry::Cheqd, it is the `ResourceReqPacket` in avida-cheqd /// For data, the contracts should have the expected type - /// In Sdjwt-Verifier, this is expected to be jwk + /// In Sdjwt-Verifier, this is expected to be the JwkInfo struct pub data_or_location: Binary, }