diff --git a/Cargo.lock b/Cargo.lock index 9897658..86e2401 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,24 +2,107 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c6d128af408d8ebd08331f0331cf2cf20d19e6c44a7aec58791641ecc8c0b5" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.1", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -47,11 +130,71 @@ dependencies = [ "half", ] +[[package]] +name = "cmw" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e7b1e30f775e494a051c8fc6d6776caee8e09033554f7844c20571da36cf06" +dependencies = [ + "base64 0.22.1", + "base64-serde", + "bitflags 1.3.2", + "hex", + "iri-string", + "lazy_static", + "mime", + "minicbor 1.1.0", + "minicbor-serde", + "once_cell", + "regex", + "reqwest", + "serde", + "serde_json", + "simple_asn1", + "thiserror 2.0.17", + "xml-rs", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "corim-rs" +version = "0.1.0" +source = "git+https://github.com/veraison/corim-rs#4d868da478ded7f99ae9b822eeb63bdb5cfcd04c" +dependencies = [ + "base64 0.22.1", + "ciborium", + "coset", + "derive_more", + "oid", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "coserv-rs" version = "0.1.0" dependencies = [ + "base64 0.22.1", + "chrono", "ciborium", + "cmw", + "corim-rs", "coset", "derive_more", "jsonwebkey", @@ -78,6 +221,15 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "deranged" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -96,217 +248,1862 @@ dependencies = [ "proc-macro2", "quote", "syn", + "unicode-xid", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "typenum", - "version_check", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "half" -version = "2.6.0" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", - "crunchy", ] [[package]] -name = "itoa" -version = "1.0.15" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "jsonwebkey" -version = "0.3.5" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57c852b14147e2bd58c14fde40398864453403ef632b1101db130282ee6e2cc" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "base64", - "bitflags", - "generic-array", - "serde", - "serde_json", - "thiserror 1.0.69", - "zeroize", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "memchr" -version = "2.7.6" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "mime" -version = "0.3.17" +name = "find-msvc-tools" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] -name = "proc-macro2" -version = "1.0.101" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "unicode-ident", + "foreign-types-shared", ] [[package]] -name = "quote" -version = "1.0.41" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ - "proc-macro2", + "percent-encoding", ] [[package]] -name = "ryu" -version = "1.0.20" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] [[package]] -name = "semver" -version = "1.0.27" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "serde" -version = "1.0.228" +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "serde_core", - "serde_derive", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "serde_derive", + "typenum", + "version_check", ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "serde_json" -version = "1.0.145" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", "itoa", - "memchr", - "ryu", - "serde", - "serde_core", ] [[package]] -name = "syn" -version = "2.0.106" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "bytes", + "http", ] [[package]] -name = "thiserror" -version = "1.0.69" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "thiserror-impl 1.0.69", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "thiserror" -version = "2.0.17" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ - "thiserror-impl 2.0.17", + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", ] [[package]] -name = "thiserror-impl" -version = "1.0.69" +name = "hyper-rustls" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "proc-macro2", - "quote", - "syn", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", ] [[package]] -name = "thiserror-impl" -version = "2.0.17" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] -name = "typenum" -version = "1.19.0" +name = "hyper-util" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] [[package]] -name = "unicode-ident" -version = "1.0.19" +name = "iana-time-zone" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] [[package]] -name = "version_check" -version = "0.9.5" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] -name = "zeroize" -version = "1.8.2" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ - "zeroize_derive", + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "zeroize_derive" -version = "1.4.2" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebkey" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57c852b14147e2bd58c14fde40398864453403ef632b1101db130282ee6e2cc" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "generic-array", + "serde", + "serde_json", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minicbor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734daad4ff3b880f23dc2a675dd74553fa8e583367aa7523f96a16e96a516b62" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a1119e42fbacc2bb65d860de6eb7c930562bc71d42dca026d06b0228231f77" + +[[package]] +name = "minicbor-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512ce2c37128698ea15c99b3518936c78a8b112b92468e7b95b9fa045666ebd8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "minicbor-serde" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80047f75e28e3b38f6ab2ec3c2c7669f6b411fa6f8424e1a90a3fd784b19a3f4" +dependencies = [ + "minicbor 2.1.3", + "serde", +] + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c19903c598813dba001b53beeae59bb77ad4892c5c1b9b3500ce4293a0d06c2" +dependencies = [ + "serde", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.17", + "time", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index fc7a61f..36758db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ derive_more = { version = "^2", features = [ "deref", "deref_mut", "constructor", + "display", ] } serde_json = {version = "1.0.140", features = [ "raw_value", @@ -38,3 +39,7 @@ jsonwebkey = "0.3.5" semver = "1.0.27" mime = "0.3.17" thiserror = "2.0.17" +corim-rs = { git = "https://github.com/veraison/corim-rs" } +chrono = "0.4.42" +cmw = "0.1.0" +base64 = "0.22.1" diff --git a/deny.toml b/deny.toml index f32de36..a7dc0a6 100644 --- a/deny.toml +++ b/deny.toml @@ -98,6 +98,8 @@ allow = [ #"Apache-2.0 WITH LLVM-exception", # Considered Copyleft, but permitted in this project "MPL-2.0", + "BlueOak-1.0.0", + "BSD-3-Clause", ] # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the @@ -241,4 +243,4 @@ allow-git = [] # 1 or more gitlab.com organizations to allow git sources for # gitlab = [""] # 1 or more bitbucket.org organizations to allow git sources for -# bitbucket = [""] \ No newline at end of file +# bitbucket = [""] diff --git a/src/coserv/common.rs b/src/coserv/common.rs new file mode 100644 index 0000000..dcc3eb5 --- /dev/null +++ b/src/coserv/common.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 + +use chrono::{format::SecondsFormat, DateTime, FixedOffset, Local}; +use corim_rs::generate_tagged; +use derive_more::From; +use std::ops::Add; + +use serde::{ + de::{Deserialize, Deserializer, Error}, + ser::{Serialize, SerializeMap, Serializer}, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize, serde::Serialize)] +pub struct Tstr(String); // Time string + +generate_tagged!(( + 0, + TaggedTstr, + Tstr, + "text string", + "representation of date/time string using CBOR tag 0" +)); + +/// Represents timestamps used in coserv query and results +#[derive(Debug, From, Default, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct TimeStamp(pub DateTime); + +/// Re-export to perform arithmetic on TimeStamp. +pub use chrono::TimeDelta; + +impl TimeStamp { + pub fn now() -> Self { + Local::now().fixed_offset().into() + } + + pub fn add(&self, delta: TimeDelta) -> Self { + self.0.add(delta).into() + } +} + +impl Serialize for TimeStamp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Section 3.3 of [RFC4287] + let tagged_time = TaggedTstr::from(Tstr(self.0.to_rfc3339_opts(SecondsFormat::Secs, true))); + tagged_time.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for TimeStamp { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let time_tstr = TaggedTstr::deserialize(deserializer)?.0 .0 .0; + Ok(TimeStamp( + DateTime::parse_from_rfc3339(&time_tstr).map_err(D::Error::custom)?, + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_time_string() { + let tests: Vec<(TimeStamp, Vec)> = vec![ + ( + TimeStamp(DateTime::parse_from_rfc3339("2020-09-04T18:34:39+05:30").unwrap()), + vec![ + 0xc0, 0x78, 0x19, // tag(0), text(25) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x38, + 0x3a, 0x33, 0x34, 0x3a, 0x33, 0x39, 0x2b, 0x30, 0x35, 0x3a, 0x33, 0x30, + ], + ), + ( + TimeStamp(DateTime::parse_from_rfc3339("2020-09-04T13:04:39+00:00").unwrap()), + vec![ + 0xc0, 0x74, // tag(0), text(20) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x33, + 0x3a, 0x30, 0x34, 0x3a, 0x33, 0x39, 0x5a, + ], + ), + ( + TimeStamp(DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z").unwrap()), + vec![ + 0xc0, 0x74, // tag(0), text(20) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x33, + 0x3a, 0x30, 0x34, 0x3a, 0x33, 0x39, 0x5a, + ], + ), + ]; + + for (i, (time, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&time, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {time:?}"); + + let time_de: TimeStamp = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!(*time, time_de, "de at index {i}: {time:?} != {time_de:?}"); + } + + let err: Result = + ciborium::from_reader([0xc0_u8, 0x63, 0x66, 0x6f, 0x6f].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_time_add() { + let base_time = DateTime::parse_from_rfc3339("2020-09-04T00:00:00Z").unwrap(); + let expected_time = DateTime::parse_from_rfc3339("2020-09-04T01:00:00Z").unwrap(); + let result = base_time.add(TimeDelta::hours(1)); + assert_eq!(result, expected_time); + } +} diff --git a/src/coserv/mod.rs b/src/coserv/mod.rs new file mode 100644 index 0000000..599efec --- /dev/null +++ b/src/coserv/mod.rs @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Implementation of the CoSERV data model +//! +//! This module contains the implementation of the CoSERV data model +//! as defined in . +//! +//! Features include +//! - Representation of CoSERV +//! - Serialization to CBOR or base64 encoded CBOR +//! - Deserialization of CoSERV in CBOR or base64 encoded CBOR. +//! +//! CoSERV re-uses many definitions from CoRIM, hence these structures +//! are directly used from . +//! For representing CMW CBOR reord, +//! is used. +//! +//! # Examples +//! +//! Create a CoSERV object and serialize to CBOR: +//! +//! ```rust +//!use coserv_rs::coserv::{ +//! ArtifactTypeChoice, Coserv, CoservBuilder, EnvironmentSelectorMap, CoservQuery, CoservQueryBuilder, +//! ResultTypeChoice, StatefulInstance, StatefulInstanceBuilder, CoservProfile, +//!}; +//! +//!use coserv_rs::coserv::corim_rs::InstanceIdTypeChoice; +//! +//!fn main() { +//! // create list of stateful instances +//! let instances: Vec = vec![ +//! StatefulInstanceBuilder::new() +//! .environment(InstanceIdTypeChoice::Bytes( +//! [0x00_u8, 0x01, 0x02].as_slice().into(), +//! )) +//! .build() +//! .unwrap(), +//! StatefulInstanceBuilder::new() +//! .environment(InstanceIdTypeChoice::Bytes( +//! [0x01_u8, 0x02, 0x03].as_slice().into(), +//! )) +//! .build() +//! .unwrap(), +//! ]; +//! +//! // create query map +//! let query = CoservQueryBuilder::new() +//! .artifact_type(ArtifactTypeChoice::ReferenceValues) +//! .result_type(ResultTypeChoice::SourceArtifacts) +//! .environment_selector(EnvironmentSelectorMap::Instance(instances)) +//! .build() +//! .unwrap(); +//! +//! // create coserv map +//! let coserv = CoservBuilder::new() +//! .profile(CoservProfile::Uri("foo".into())) +//! .query(query) +//! .build() +//! .unwrap(); +//! +//! let coserv_cbor = coserv.to_cbor().unwrap(); +//!} +//! +//! ``` +//! +//! Deserialize CBOR encoded CoSERV and generate response: +//! +//! ```rust +//!use coserv_rs::coserv::{Coserv, CoservBuilder, EnvironmentSelectorMap}; +//! +//!use coserv_rs::coserv::{TimeDelta, TimeStamp}; +//! +//!use coserv_rs::coserv::{ +//! CoservResultBuilder, ReferenceValuesQuad, ReferenceValuesQuadBuilder, ReferenceValuesResult, +//! ResultSetTypeChoice, +//!}; +//! +//!use coserv_rs::coserv::corim_rs::{ +//! CryptoKeyTypeChoice, EnvironmentMap, MeasurementMap, MeasurementValuesMapBuilder, +//! ReferenceTripleRecord, +//!}; +//! +//!fn main() { +//! let cbor_data: Vec = vec![ +//! 0xA2, 0x00, 0x63, 0x66, 0x6F, 0x6F, 0x01, 0xA4, 0x00, 0x02, 0x01, 0xA1, 0x01, 0x82, 0x81, +//! 0xD9, 0x02, 0x30, 0x43, 0x00, 0x01, 0x02, 0x81, 0xD9, 0x02, 0x30, 0x43, 0x01, 0x02, 0x03, +//! 0x02, 0xC0, 0x78, 0x19, 0x32, 0x30, 0x32, 0x35, 0x2D, 0x31, 0x30, 0x2D, 0x32, 0x37, 0x54, +//! 0x31, 0x39, 0x3A, 0x31, 0x31, 0x3A, 0x33, 0x30, 0x2B, 0x30, 0x35, 0x3A, 0x33, 0x30, 0x03, +//! 0x01, +//! ]; +//! +//! let de_coserv = Coserv::from_cbor(cbor_data.as_slice()).unwrap(); +//! // check artifact type and result type, then direct to +//! // the correct handler +//! +//! // check environment selector type, then direct to +//! // the correct handler +//! let mut rv_quads: Vec = vec![]; +//! +//! match de_coserv.query.environment_selector { +//! EnvironmentSelectorMap::Instance(ref v) => { +//! for si in v.iter() { +//! let mut ref_env = EnvironmentMap::default(); +//! ref_env.instance = Some(si.environment.clone()); +//! +//! // gather measurements for the environment +//! let mval_map = MeasurementValuesMapBuilder::new() +//! .name("foo".into()) +//! .build() +//! .unwrap(); +//! let mut meas_map = MeasurementMap::default(); +//! meas_map.mval = mval_map; +//! let rv_triple = ReferenceTripleRecord { +//! ref_env: ref_env, +//! ref_claims: vec![meas_map], +//! }; +//! let rv_quad = ReferenceValuesQuadBuilder::new() +//! .triple(rv_triple) +//! .authorities(vec![CryptoKeyTypeChoice::Bytes( +//! [0x00_u8, 0x01].as_slice().into(), +//! )]) +//! .build() +//! .unwrap(); +//! rv_quads.push(rv_quad); +//! } +//! } +//! _ => panic!(), +//! } +//! +//! let ref_vals_results = ReferenceValuesResult { rv_quads: rv_quads }; +//! +//! // build result set +//! let results = CoservResultBuilder::new() +//! .expiry(TimeStamp::now().add(TimeDelta::days(10))) +//! .result_set(ResultSetTypeChoice::ReferenceValues(ref_vals_results)) +//! .build() +//! .unwrap(); +//! +//! // build response +//! let response = CoservBuilder::new() +//! .profile(de_coserv.profile) +//! .query(de_coserv.query) +//! .results(results) +//! .build() +//! .unwrap(); +//! +//! // serialize to cbor +//! let response_cbor = response.to_cbor().unwrap(); +//!} +//! ``` + +use crate::error::CoservError; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use serde::{ + de::{self, Deserialize, Deserializer, Error, MapAccess, Visitor}, + ser::{Serialize, SerializeMap, Serializer}, +}; +use std::fmt; +use std::io::Read; +use std::marker::PhantomData; + +// Contains structures used by both query and result +mod common; + +// data types used in CoSERV query +mod query; + +// data types used in CoSERV response +mod result; + +// re-export to simplify the API +pub use common::*; +pub use query::*; +pub use result::*; + +pub use corim_rs; + +/// Representation of OID. +/// Use [ObjectIdentifier::try_from<&Vec>] to construct from BER encoding +/// or [ObjectIdentifier::try_from<&str>] to construct from dot decimal form. +pub use corim_rs::ObjectIdentifier; + +pub use cmw; + +/// Represents a CoSERV object +#[derive(Debug, PartialEq)] +pub struct Coserv<'a> { + /// CoSERV profile + pub profile: CoservProfile, + /// CoSERV query map + pub query: CoservQuery<'a>, + /// optional CoSERV result map + pub results: Option>, +} + +impl<'a> Coserv<'a> { + /// Marshal CoSERV object to CBOR + /// deterministically encoded: + pub fn to_cbor(&self) -> Result, CoservError> { + let mut buf = Vec::::new(); + ciborium::into_writer(&self, &mut buf).map_err(CoservError::custom)?; + Ok(buf) + } + + /// Unmarshal CBOR into CoSERV object + pub fn from_cbor(src: R) -> Result { + ciborium::from_reader(src).map_err(CoservError::custom) + } + + /// Generate URL safe base64 encoding of the CBOR encoded CoSERV oject + pub fn to_b64_url(&self) -> Result { + let cbor = self.to_cbor()?; + let b64 = URL_SAFE_NO_PAD.encode(cbor.as_slice()); + Ok(b64) + } + + /// Create CoSERV object from URL safe base64 encoded CBOR CoSERV + pub fn from_b64_url(b64: &[u8]) -> Result { + let cbor = URL_SAFE_NO_PAD.decode(b64).map_err(CoservError::custom)?; + Self::from_cbor(cbor.as_slice()) + } +} + +/// Builder for CoSERV object +#[derive(Debug, Default)] +pub struct CoservBuilder<'a> { + pub profile: Option, + pub query: Option>, + pub results: Option>, +} + +impl<'a> CoservBuilder<'a> { + pub fn new() -> Self { + Self::default() + } + + /// Set the profile + pub fn profile(mut self, value: CoservProfile) -> Self { + self.profile = Some(value); + self + } + + /// Set the query + pub fn query(mut self, value: CoservQuery<'a>) -> Self { + self.query = Some(value); + self + } + + /// Set the results + pub fn results(mut self, value: CoservResult<'a>) -> Self { + self.results = Some(value); + self + } + + /// Method to build the CoSERV object from the builder + pub fn build(self) -> Result, CoservError> { + // TODO: check if query artifact type and result type + // matches those present in result + Ok(Coserv { + profile: self.profile.ok_or(CoservError::RequiredFieldNotSet( + "profile".into(), + "coserv".into(), + ))?, + query: self.query.ok_or(CoservError::RequiredFieldNotSet( + "query".into(), + "coserv".into(), + ))?, + results: self.results, + }) + } +} + +impl Serialize for Coserv<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let num_elts = 2 + self.results.is_some() as usize; + let mut map = serializer.serialize_map(Some(num_elts))?; + map.serialize_entry(&0, &self.profile)?; + map.serialize_entry(&1, &self.query)?; + if let Some(res) = &self.results { + map.serialize_entry(&2, res)?; + } + map.end() + } +} + +impl<'de> Deserialize<'de> for Coserv<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CoservVisitor<'a> { + marker: PhantomData<&'a str>, + } + impl<'de, 'a> Visitor<'de> for CoservVisitor<'a> { + type Value = Coserv<'a>; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "map containing CoSERV fields") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut builder = CoservBuilder::new(); + loop { + match access.next_key::()? { + Some(0) => { + builder = builder.profile(access.next_value::()?); + } + Some(1) => { + builder = builder.query(access.next_value::()?); + } + Some(2) => { + builder = builder.results(access.next_value::()?); + } + Some(n) => { + return Err(de::Error::unknown_field( + n.to_string().as_str(), + &["0", "1", "2"], + )); + } + None => break, + }; + } + builder.build().map_err(M::Error::custom) + } + } + deserializer.deserialize_map(CoservVisitor { + marker: PhantomData, + }) + } +} + +/// Represents CoSERV profile +#[derive(PartialEq, Debug)] +pub enum CoservProfile { + /// Byte string representing ASN1 Object Identifier. + /// Gets serialized as CBOR byte string. + Oid(ObjectIdentifier), + /// Text string representing URI + /// Gets serialized as CBOR text string. + Uri(String), +} + +impl Serialize for CoservProfile { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + CoservProfile::Oid(oid) => oid.serialize(serializer), + CoservProfile::Uri(uri) => uri.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for CoservProfile { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match ciborium::Value::deserialize(deserializer)? { + ciborium::Value::Text(uri) => Ok(Self::Uri(uri)), + ciborium::Value::Bytes(oid) => { + Ok(Self::Oid(oid.try_into().map_err(|_| { + D::Error::custom("cannot convert bytes to OID") + })?)) + } + _ => Err(D::Error::custom("invalid profile type choice in cbor")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::path::PathBuf; + + #[test] + fn test_valid_cbor() { + let tests = [ + "example-class-selector-noindent", + "example-class-selector", + "example-group-selector", + "example-instance-selector", + "rv-class-simple-results-source-artifacts", + "rv-class-simple-results", + "rv-class-simple", + "rv-class-stateful", + "rv-results", + ]; + + let mut path = PathBuf::from("testdata"); + + for case in tests.iter() { + path.push(case); + path.set_extension("cbor"); + let cbor = fs::read(&path).unwrap(); + let coserv = Coserv::from_cbor(cbor.as_slice()).unwrap(); + let cbor_ser = coserv.to_cbor().unwrap(); + assert_eq!(cbor_ser, cbor); + path.pop(); + } + } + + #[test] + fn test_valid_b64() { + let tests = [ + "example-class-selector-noindent", + "example-class-selector", + "example-group-selector", + "example-instance-selector", + "rv-class-simple-results-source-artifacts", + "rv-class-simple-results", + "rv-class-simple", + "rv-class-stateful", + "rv-results", + ]; + + let mut path = PathBuf::from("testdata"); + + for case in tests.iter() { + path.push(case); + path.set_extension("b64u"); + let b64u = fs::read(&path).unwrap(); + let coserv = Coserv::from_b64_url(b64u.as_slice()).unwrap(); + let b64u_ser = coserv.to_b64_url().unwrap(); + assert_eq!(Vec::::from(b64u_ser.as_bytes()), b64u); + path.pop(); + } + } + + #[test] + fn test_builder() { + let builder = CoservBuilder::new(); + assert!(builder.build().is_err()); + + let mut builder = CoservBuilder::new(); + builder = builder.profile(CoservProfile::Uri("foo".into())); + assert!(builder.build().is_err()); + } + + #[test] + fn test_invalid() { + let cbor: Vec = vec![0xa1, 0x04, 0x01]; + let err: Result = ciborium::from_reader(cbor.as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_profile() { + let cbor: Vec = vec![0xd8, 0x20, 0x63, 0x66, 0x6f, 0x6f]; // tagged (32) type + let err: Result = ciborium::from_reader(cbor.as_slice()); + assert!(err.is_err()); + + let tests: Vec<(CoservProfile, Vec)> = vec![ + ( + CoservProfile::Uri(String::from("foo")), + vec![0x63, 0x66, 0x6f, 0x6f], + ), + ( + CoservProfile::Oid(vec![0x2a_u8, 0x03, 0x04].try_into().unwrap()), + vec![0x43, 0x2a, 0x03, 0x04], + ), // 1.2.3.4 + ( + CoservProfile::Oid("1.2.3.4".try_into().unwrap()), + vec![0x43, 0x2a, 0x03, 0x04], + ), // 1.2.3.4 + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + } + } +} diff --git a/src/coserv/query.rs b/src/coserv/query.rs new file mode 100644 index 0000000..66b228a --- /dev/null +++ b/src/coserv/query.rs @@ -0,0 +1,1084 @@ +// SPDX-License-Identifier: Apache-2.0 + +use super::common::TimeStamp; +use crate::error::CoservError; +use corim_rs::triples::{ClassMap, GroupIdTypeChoice, InstanceIdTypeChoice, MeasurementMap}; +use serde::{ + de::{self, Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}, + ser::{Serialize, SerializeMap, SerializeSeq, Serializer}, +}; +use std::fmt; +use std::marker::PhantomData; + +/// Representation of CoSERV query map. +/// Use [CoservQueryBuilder] to build this struct. +#[derive(Debug, PartialEq)] +pub struct CoservQuery<'a> { + /// Query artifact type + pub artifact_type: ArtifactTypeChoice, + /// environment selector map + pub environment_selector: EnvironmentSelectorMap<'a>, + /// timestamp of query + pub timestamp: TimeStamp, + /// result type selector + pub result_type: ResultTypeChoice, +} + +/// Builder for [CoservQuery] +#[derive(Debug, Default)] +pub struct CoservQueryBuilder<'a> { + pub artifact_type: Option, + pub environment_selector: Option>, + pub timestamp: Option, + pub result_type: Option, +} + +impl<'a> CoservQueryBuilder<'a> { + pub fn new() -> Self { + Self::default() + } + + pub fn artifact_type(mut self, value: ArtifactTypeChoice) -> Self { + self.artifact_type = Some(value); + self + } + + pub fn environment_selector(mut self, value: EnvironmentSelectorMap<'a>) -> Self { + self.environment_selector = Some(value); + self + } + + // this function is used only by the deserializer. + // While building, this should be set to `now` + fn timestamp(mut self, value: TimeStamp) -> Self { + self.timestamp = Some(value); + self + } + + pub fn result_type(mut self, value: ResultTypeChoice) -> Self { + self.result_type = Some(value); + self + } + + pub fn build(self) -> Result, CoservError> { + Ok(CoservQuery { + artifact_type: self.artifact_type.ok_or(CoservError::RequiredFieldNotSet( + "artifact type".into(), + "query".into(), + ))?, + environment_selector: self.environment_selector.ok_or( + CoservError::RequiredFieldNotSet("environment selector".into(), "query".into()), + )?, + timestamp: self.timestamp.unwrap_or(TimeStamp::now()), + result_type: self.result_type.ok_or(CoservError::RequiredFieldNotSet( + "result type".into(), + "query".into(), + ))?, + }) + } +} + +impl Serialize for CoservQuery<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(4))?; + map.serialize_entry(&0, &self.artifact_type)?; + map.serialize_entry(&1, &self.environment_selector)?; + map.serialize_entry(&2, &self.timestamp)?; + map.serialize_entry(&3, &self.result_type)?; + map.end() + } +} + +impl<'de> Deserialize<'de> for CoservQuery<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct QueryVisitor<'a> { + marker: PhantomData<&'a str>, + } + impl<'de, 'a> Visitor<'de> for QueryVisitor<'a> { + type Value = CoservQuery<'a>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "map containing CoSERV query (Query)") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut builder = CoservQueryBuilder::new(); + loop { + match access.next_key::()? { + Some(0) => { + builder = + builder.artifact_type(access.next_value::()?); + } + Some(1) => { + builder = builder.environment_selector( + access.next_value::>()?, + ); + } + Some(2) => { + builder = builder.timestamp(access.next_value::()?); + } + Some(3) => { + builder = builder.result_type(access.next_value::()?); + } + Some(n) => Err(de::Error::unknown_field( + n.to_string().as_str(), + &["0", "1", "2", "3"], + ))?, + None => break, + }; + } + // decoded query without timestamp is invalid + if builder.timestamp.is_none() { + return Err(M::Error::custom( + "Required field timestamp not present in query", + )); + } + builder.build().map_err(M::Error::custom) + } + } + + deserializer.deserialize_map(QueryVisitor { + marker: PhantomData, + }) + } +} + +/// artifact type queried +#[derive(Debug, PartialEq)] +pub enum ArtifactTypeChoice { + EndorsedValues, + TrustAnchors, + ReferenceValues, +} + +impl From<&ArtifactTypeChoice> for i64 { + fn from(value: &ArtifactTypeChoice) -> Self { + match value { + ArtifactTypeChoice::EndorsedValues => 0, + ArtifactTypeChoice::TrustAnchors => 1, + ArtifactTypeChoice::ReferenceValues => 2, + } + } +} + +impl TryFrom for ArtifactTypeChoice { + type Error = &'static str; + + fn try_from(value: i64) -> Result { + match value { + 0 => Ok(ArtifactTypeChoice::EndorsedValues), + 1 => Ok(ArtifactTypeChoice::TrustAnchors), + 2 => Ok(ArtifactTypeChoice::ReferenceValues), + _ => Err("unknown field for artifact type"), + } + } +} + +impl Serialize for ArtifactTypeChoice { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_i64(self.into()) + } +} + +impl<'de> Deserialize<'de> for ArtifactTypeChoice { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + i64::deserialize(deserializer)? + .try_into() + .map_err(D::Error::custom) + } +} + +/// Environment selector for query +/// Can be either class-based or instance-based or group-based. +#[derive(Debug, PartialEq)] +pub enum EnvironmentSelectorMap<'a> { + Class(Vec>), + Instance(Vec>), + Group(Vec>), +} + +impl Serialize for EnvironmentSelectorMap<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(1))?; + match self { + EnvironmentSelectorMap::Class(cl) => map.serialize_entry(&0, cl), + EnvironmentSelectorMap::Instance(inst) => map.serialize_entry(&1, inst), + EnvironmentSelectorMap::Group(grp) => map.serialize_entry(&2, grp), + }?; + map.end() + } +} + +impl<'de> Deserialize<'de> for EnvironmentSelectorMap<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EnvironmentSelectorMapVisitor<'a> { + marker: PhantomData<&'a str>, + } + + impl<'de, 'a> Visitor<'de> for EnvironmentSelectorMapVisitor<'a> { + type Value = EnvironmentSelectorMap<'a>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CoSERV environment selector map") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + match access.next_key::()? { + Some(0) => Ok(Self::Value::Class( + access.next_value::>>()?, + )), + Some(1) => Ok(Self::Value::Instance( + access.next_value::>>()?, + )), + Some(2) => Ok(Self::Value::Group( + access.next_value::>>()?, + )), + Some(n) => Err(M::Error::unknown_field( + n.to_string().as_str(), + &["0", "1", "2"], + )), + None => Err(M::Error::custom("malformed environment selector map")), + } + } + } + deserializer.deserialize_map(EnvironmentSelectorMapVisitor { + marker: PhantomData, + }) + } +} + +/// representation of stateful class. +/// Use [StatefulClassBuilder] to build this. +pub type StatefulClass<'a> = StatefulEnvironment<'a, ClassMap<'a>>; + +/// representation of stateful instance +/// Use [StatefulInstanceBuilder] to build this. +pub type StatefulInstance<'a> = StatefulEnvironment<'a, InstanceIdTypeChoice<'a>>; + +/// representation of stateful group +/// Use [StatefulGroupBuilder] to build this. +pub type StatefulGroup<'a> = StatefulEnvironment<'a, GroupIdTypeChoice<'a>>; + +/// builder for [StatefulClass] +pub type StatefulClassBuilder<'a> = StatefulEnvironmentBuilder<'a, ClassMap<'a>>; + +/// builder for [StatefulInstance] +pub type StatefulInstanceBuilder<'a> = StatefulEnvironmentBuilder<'a, InstanceIdTypeChoice<'a>>; + +/// builder for [StatefulGroup] +pub type StatefulGroupBuilder<'a> = StatefulEnvironmentBuilder<'a, GroupIdTypeChoice<'a>>; + +// empty private trait that should be implemented for +// valid environments in coserv +trait AcceptedCoservEnvironment {} + +impl AcceptedCoservEnvironment for ClassMap<'_> {} +impl AcceptedCoservEnvironment for InstanceIdTypeChoice<'_> {} +impl AcceptedCoservEnvironment for GroupIdTypeChoice<'_> {} + +/// Generic object for stateful environment. +/// Use [StatefulClass], [StatefulInstance], [StatefulGroup] +/// instead of this. +/// +/// Note: definitions of this cannot be made outside of this +/// crate because of private bounds on T +#[allow(private_bounds)] +#[derive(Debug, PartialEq)] +pub struct StatefulEnvironment<'a, T> +where + T: Serialize + AcceptedCoservEnvironment, +{ + /// environment + pub environment: T, + /// state + pub measurements: Option>>, +} + +/// Builder for [StatefulEnvironment]. +/// Use [StatefulClassBuilder], [StatefulInstanceBuilder], [StatefulGroupBuilder] +/// instead of this. +#[derive(Debug)] +#[allow(private_bounds)] +pub struct StatefulEnvironmentBuilder<'a, T> +where + T: Serialize + AcceptedCoservEnvironment, +{ + environment: Option, + measurements: Option>>, +} + +impl Default for StatefulEnvironmentBuilder<'_, T> +where + T: Serialize + AcceptedCoservEnvironment, +{ + fn default() -> Self { + StatefulEnvironmentBuilder { + environment: None, + measurements: None, + } + } +} + +#[allow(private_bounds)] +impl<'a, T> StatefulEnvironmentBuilder<'a, T> +where + T: Serialize + AcceptedCoservEnvironment, +{ + pub fn new() -> Self { + Self::default() + } + + pub fn environment(mut self, value: T) -> Self { + self.environment = Some(value); + self + } + + pub fn add_measurement(mut self, value: MeasurementMap<'a>) -> Self { + if let Some(ref mut v) = self.measurements { + v.push(value); + } else { + self.measurements = Some(vec![value]); + } + self + } + + pub fn measurements(mut self, value: Vec>) -> Self { + self.measurements = Some(value); + self + } + + pub fn build(self) -> Result, CoservError> { + if self.environment.is_none() { + return Err(CoservError::RequiredFieldNotSet( + "environment".into(), + "stateful environment".into(), + )); + } + Ok(StatefulEnvironment { + environment: self.environment.ok_or(CoservError::RequiredFieldNotSet( + "environment".into(), + "stateful environment".into(), + ))?, + measurements: self.measurements, + }) + } +} + +impl Serialize for StatefulEnvironment<'_, T> +where + T: Serialize + AcceptedCoservEnvironment, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // should not use indefinite length encoding + let num_elts = 1 + // environment + self.measurements.is_some() as usize; // measurements + let mut seq = serializer.serialize_seq(Some(num_elts))?; + seq.serialize_element(&self.environment)?; + if let Some(meas) = &self.measurements { + seq.serialize_element(meas)?; + } + seq.end() + } +} + +impl<'a, 'de, T> Deserialize<'de> for StatefulEnvironment<'a, T> +where + T: Deserialize<'de> + Serialize + AcceptedCoservEnvironment + 'a, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(StatefulEnvVisitor::<'a, T> { + marker: PhantomData, + }) + } +} + +struct StatefulEnvVisitor<'a, T> { + marker: PhantomData<&'a T>, +} + +impl<'de, 'a, T> Visitor<'de> for StatefulEnvVisitor<'a, T> +where + T: Serialize + Deserialize<'de> + AcceptedCoservEnvironment + 'a, +{ + type Value = StatefulEnvironment<'a, T>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CoSERV stateful environment") + } + + fn visit_seq(self, mut access: S) -> Result + where + S: SeqAccess<'de>, + { + let mut builder = StatefulEnvironmentBuilder::new(); + let env = access + .next_element::()? + .ok_or(S::Error::custom("environment identifier cannot be empty"))?; + builder = builder.environment(env); + if let Some(meas) = access.next_element::>>()? { + builder = builder.measurements(meas); + } + builder.build().map_err(S::Error::custom) + } +} + +/// Result type queried: source artifacts, collected artifacts +/// or both +#[derive(Debug, PartialEq)] +pub enum ResultTypeChoice { + CollectedArtifacts, + SourceArtifacts, + Both, +} + +impl From<&ResultTypeChoice> for i64 { + fn from(value: &ResultTypeChoice) -> Self { + match value { + ResultTypeChoice::CollectedArtifacts => 0, + ResultTypeChoice::SourceArtifacts => 1, + ResultTypeChoice::Both => 2, + } + } +} + +impl TryFrom for ResultTypeChoice { + type Error = &'static str; + + fn try_from(value: i64) -> Result { + match value { + 0 => Ok(ResultTypeChoice::CollectedArtifacts), + 1 => Ok(ResultTypeChoice::SourceArtifacts), + 2 => Ok(ResultTypeChoice::Both), + _ => Err("unknown field"), + } + } +} + +impl Serialize for ResultTypeChoice { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_i64(self.into()) + } +} + +impl<'de> Deserialize<'de> for ResultTypeChoice { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + i64::deserialize(deserializer)? + .try_into() + .map_err(D::Error::custom) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::DateTime; + use corim_rs::triples::MeasurementValuesMap; + use corim_rs::{Bytes, ClassIdTypeChoice}; + #[test] + fn test_artifact_type_choice() { + let tests: Vec<(ArtifactTypeChoice, Vec)> = vec![ + (ArtifactTypeChoice::EndorsedValues, vec![0x00]), + (ArtifactTypeChoice::TrustAnchors, vec![0x01]), + (ArtifactTypeChoice::ReferenceValues, vec![0x02]), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: ArtifactTypeChoice = + ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + assert!(ArtifactTypeChoice::try_from(3).is_err()); + } + + #[test] + fn test_result_type_choice() { + let tests: Vec<(ResultTypeChoice, Vec)> = vec![ + (ResultTypeChoice::CollectedArtifacts, vec![0x00]), + (ResultTypeChoice::SourceArtifacts, vec![0x01]), + (ResultTypeChoice::Both, vec![0x02]), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: ResultTypeChoice = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = ciborium::from_reader([0x03_u8].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_stateful_class() { + let (m1, m2) = ( + MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }, + MeasurementValuesMap { + name: Some("bar".into()), + ..Default::default() + }, + ); + let tests: Vec<(StatefulClass, Vec)> = vec![ + ( + StatefulClass { + environment: ClassMap { + class_id: Some(ClassIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + vendor: None, + model: None, + layer: None, + index: None, + }, + measurements: None, + }, + vec![ + 0x81, // array(1) + 0xa1, // map(1) + 0x00, // unsigned(0) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, + ], + ), + ( + StatefulClass { + environment: ClassMap { + class_id: Some(ClassIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + vendor: None, + model: None, + layer: None, + index: None, + }, + measurements: Some(vec![ + MeasurementMap { + mkey: None, + mval: m1, + authorized_by: None, + }, + MeasurementMap { + mkey: None, + mval: m2, + authorized_by: None, + }, + ]), + }, + vec![ + 0x82, // array(2) + 0xa1, // map(1) + 0x00, // unsigned(0) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x62, 0x61, 0x72, + ], + ), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: StatefulClass = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = ciborium::from_reader([0x80_u8].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_stateful_instance() { + let (m1, m2) = ( + MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }, + MeasurementValuesMap { + name: Some("bar".into()), + ..Default::default() + }, + ); + let tests: Vec<(StatefulInstance, Vec)> = vec![ + ( + StatefulInstance { + environment: InstanceIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: None, + }, + vec![ + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + ], + ), + ( + StatefulInstance { + environment: InstanceIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: Some(vec![ + MeasurementMap { + mkey: None, + mval: m1, + authorized_by: None, + }, + MeasurementMap { + mkey: None, + mval: m2, + authorized_by: None, + }, + ]), + }, + vec![ + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x62, 0x61, 0x72, // "bar" + ], + ), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: StatefulInstance = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = ciborium::from_reader([0x80_u8].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_stateful_group() { + // redundant + let (m1, m2) = ( + MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }, + MeasurementValuesMap { + name: Some("bar".into()), + ..Default::default() + }, + ); + let tests: Vec<(StatefulGroup, Vec)> = vec![ + ( + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: None, + }, + vec![ + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + ], + ), + ( + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: Some(vec![ + MeasurementMap { + mkey: None, + mval: m1, + authorized_by: None, + }, + MeasurementMap { + mkey: None, + mval: m2, + authorized_by: None, + }, + ]), + }, + vec![ + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x62, 0x61, 0x72, // "bar" + ], + ), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: StatefulGroup = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = ciborium::from_reader([0x80_u8].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_environment_selector_map() { + let m1 = MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }; + let tests: Vec<(EnvironmentSelectorMap, Vec)> = vec![ + ( + EnvironmentSelectorMap::Class(vec![ + StatefulClass { + environment: ClassMap { + class_id: Some(ClassIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + vendor: None, + model: None, + layer: None, + index: None, + }, + measurements: None, + }, + StatefulClass { + environment: ClassMap { + class_id: Some(ClassIdTypeChoice::Bytes( + Bytes::from(vec![2, 3, 4, 5]).into(), + )), + vendor: None, + model: None, + layer: None, + index: None, + }, + measurements: Some(vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }]), + }, + ]), + vec![ + 0xa1, // map(1) + 0x00, // unsigned(0) + 0x82, // array(2) + 0x81, // array(1) + 0xa1, // map(1) + 0x00, // unsigned(0) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xa1, // map(1) + 0x00, // unsigned(0) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + ), + ( + EnvironmentSelectorMap::Instance(vec![ + StatefulInstance { + environment: InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + ), + measurements: None, + }, + StatefulInstance { + environment: InstanceIdTypeChoice::Bytes( + Bytes::from(vec![2, 3, 4, 5]).into(), + ), + measurements: Some(vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }]), + }, + ]), + vec![ + 0xa1, // map(1) + 0x01, // unsigned(0) + 0x82, // array(2) + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + ), + ( + EnvironmentSelectorMap::Group(vec![ + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: None, + }, + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + measurements: Some(vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }]), + }, + ]), + vec![ + 0xa1, // map(1) + 0x02, // unsigned(0) + 0x82, // array(2) + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + ), + ]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: EnvironmentSelectorMap = + ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = ciborium::from_reader([0xa0_u8].as_slice()); + assert!(err.is_err()); + + let err: Result = + ciborium::from_reader([0xa1, 0x03, 0x80].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_query() { + let m1 = MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }; + let tests: Vec<(CoservQuery, Vec)> = vec![( + CoservQuery { + artifact_type: ArtifactTypeChoice::ReferenceValues, + environment_selector: EnvironmentSelectorMap::Group(vec![ + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + measurements: None, + }, + StatefulGroup { + environment: GroupIdTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + measurements: Some(vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }]), + }, + ]), + timestamp: DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + result_type: ResultTypeChoice::CollectedArtifacts, + }, + vec![ + 0xa4, // map(4) + 0x00, // unsigned(0) + 0x02, // unsigned(2) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x02, // unsigned(2) + 0x82, // array(2) + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0x02, // unsigned(2) + 0xc0, 0x74, // text(20) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x33, 0x3a, + 0x30, 0x34, 0x3a, 0x33, 0x39, 0x5a, // "2020-09-04T13:04:39Z" + 0x03, // unsigned(3) + 0x00, // unsigned(0) + ], + )]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: CoservQuery = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let cbor_invalid_key: Vec = vec![0xa1, 0x04, 0x80]; + let err: Result = ciborium::from_reader(cbor_invalid_key.as_slice()); + assert!(err.is_err()); + + let cbor_missing_timestamp: Vec = vec![ + 0xa3, // map(3) + 0x00, // unsigned(0) + 0x02, // unsigned(2) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x02, // unsigned(2) + 0x82, // array(2) + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0x03, // unsigned(3) + 0x00, // unsigned(0) + ]; + let err: Result = ciborium::from_reader(cbor_missing_timestamp.as_slice()); + assert!(err.is_err()); + } +} diff --git a/src/coserv/result.rs b/src/coserv/result.rs new file mode 100644 index 0000000..82ee1fb --- /dev/null +++ b/src/coserv/result.rs @@ -0,0 +1,1291 @@ +// SPDX-License-Identifier: Apache-2.0 + +use super::common::TimeStamp; +use crate::error::CoservError; +use cmw::CMW; +use corim_rs::core::{ExtensionMap, ExtensionValue}; +use corim_rs::triples::{ + AttestKeyTripleRecord, ConditionalEndorsementTripleRecord, CryptoKeyTypeChoice, + EndorsedTripleRecord, ReferenceTripleRecord, +}; +use derive_more::Display; +use serde::{ + de::{Deserialize, Deserializer, Error, MapAccess, Visitor}, + ser::{Error as SerError, Serialize, SerializeMap, Serializer}, +}; +use std::borrow::Cow; +use std::fmt; +use std::marker::PhantomData; + +/// Representation of CoSERV result. +/// Use [CoservResultBuilder] to build this. +#[derive(Debug, PartialEq, Clone)] +pub struct CoservResult<'a> { + /// result set + pub result_set: Option>, + /// result set expiry + pub expiry: TimeStamp, + /// optional field for source artifacts + pub source_artifacts: Option>, +} + +impl Serialize for CoservResult<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // for fixed length encoding of map + let num_elts = 1 + + self.source_artifacts.is_some() as usize + + match &self.result_set { + Some(rs) => match rs { + ResultSetTypeChoice::ReferenceValues(_) + | ResultSetTypeChoice::TrustAnchors(_) => 1, + ResultSetTypeChoice::EndorsedValues(_) => 2, + ResultSetTypeChoice::Extensions(m) => m.0.len(), + }, + None => 0, + }; + + let mut map = serializer.serialize_map(Some(num_elts))?; + if let Some(rs) = &self.result_set { + match rs { + ResultSetTypeChoice::ReferenceValues(rv) => { + map.serialize_entry(&0, &rv.rv_quads)?; + } + ResultSetTypeChoice::EndorsedValues(ev) => { + map.serialize_entry(&1, &ev.ev_quads)?; + map.serialize_entry(&2, &ev.cev_quads)?; + } + ResultSetTypeChoice::TrustAnchors(ta) => { + map.serialize_entry(&3, &ta.ak_quads)?; + } + ResultSetTypeChoice::Extensions(ext) => { + ext.serialize_map(&mut map, false)?; + } + }; + } + map.serialize_entry(&10, &self.expiry)?; + if self.source_artifacts.is_some() { + let wrapper_cmw: Vec = self + .source_artifacts + .as_ref() + .unwrap() + .iter() + .map(|x| CmwCborRecordType(Cow::Borrowed(x))) + .collect(); + map.serialize_entry(&11, &wrapper_cmw)?; + } + map.end() + } +} + +impl<'de> Deserialize<'de> for CoservResult<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CoservResultVisitor<'a> { + marker: PhantomData<&'a str>, + } + + impl<'de, 'a> Visitor<'de> for CoservResultVisitor<'a> { + type Value = CoservResult<'a>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CoSERV result map") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut builder = CoservResultBuilder::new(); + loop { + match access.next_key::()? { + Some(0) => { + builder + .rv_quads(access.next_value::>>()?) + .map_err(M::Error::custom)?; + } + Some(1) => { + builder + .ev_quads(access.next_value::>>()?) + .map_err(M::Error::custom)?; + } + Some(2) => { + builder + .cev_quads( + access.next_value::>>()?, + ) + .map_err(M::Error::custom)?; + } + Some(3) => { + builder + .ak_quads(access.next_value::>>()?) + .map_err(M::Error::custom)?; + } + Some(4) => Err(M::Error::custom("CoTS unimplemented"))?, + Some(10) => builder = builder.expiry(access.next_value::()?), + Some(11) => { + let source_artifacts = access.next_value::>()?; + let cmws: Vec = source_artifacts + .into_iter() + .map(|x| x.0.into_owned()) + .collect(); + builder = builder.source_artifacts(cmws); + } + Some(n) => { + builder + .add_extension(n.into(), access.next_value::()?) + .map_err(M::Error::custom)?; + } + None => break, + } + } + builder.build().map_err(M::Error::custom) + } + } + deserializer.deserialize_map(CoservResultVisitor { + marker: PhantomData, + }) + } +} + +/// Builder for [CoservResult] +#[derive(Debug, Default, Clone)] +pub struct CoservResultBuilder<'a> { + pub result_set: Option>, + pub expiry: Option, + pub source_artifacts: Option>, +} + +impl<'a> CoservResultBuilder<'a> { + pub fn new() -> Self { + Self::default() + } + + // these methods are used by deserializer to construct the result set + // while building coserv result, the result set can be completely + // built and then set using the result_set method + fn rv_quads(&mut self, value: Vec>) -> Result<(), CoservError> { + if let Some(ref mut res) = self.result_set { + match res { + ResultSetTypeChoice::ReferenceValues(ref mut rv) => { + rv.rv_quads = value; + Ok(()) + } + other => Err(CoservError::SetQuadsFailed( + "rv_quads".to_string(), + other.to_string(), + )), + } + } else { + self.result_set = Some(ResultSetTypeChoice::ReferenceValues( + ReferenceValuesResult { rv_quads: value }, + )); + Ok(()) + } + } + + fn ak_quads(&mut self, value: Vec>) -> Result<(), CoservError> { + if let Some(ref mut res) = self.result_set { + match res { + ResultSetTypeChoice::TrustAnchors(ref mut ak) => { + ak.ak_quads = value; + Ok(()) + } + other => Err(CoservError::SetQuadsFailed( + "ak_quads".to_string(), + other.to_string(), + )), + } + } else { + self.result_set = Some(ResultSetTypeChoice::TrustAnchors(TrustAnchorsResult { + ak_quads: value, + })); + Ok(()) + } + } + + fn ev_quads(&mut self, value: Vec>) -> Result<(), CoservError> { + if let Some(ref mut res) = self.result_set { + match res { + ResultSetTypeChoice::EndorsedValues(ref mut ev) => { + ev.ev_quads = value; + Ok(()) + } + other => Err(CoservError::SetQuadsFailed( + "ev_quads".to_string(), + other.to_string(), + )), + } + } else { + self.result_set = Some(ResultSetTypeChoice::EndorsedValues(EndorsedValuesResult { + ev_quads: value, + cev_quads: vec![], + })); + Ok(()) + } + } + + fn cev_quads(&mut self, value: Vec>) -> Result<(), CoservError> { + if let Some(ref mut res) = self.result_set { + match res { + ResultSetTypeChoice::EndorsedValues(ref mut ev) => { + ev.cev_quads = value; + Ok(()) + } + other => Err(CoservError::SetQuadsFailed( + "cev_quads".to_string(), + other.to_string(), + )), + } + } else { + self.result_set = Some(ResultSetTypeChoice::EndorsedValues(EndorsedValuesResult { + ev_quads: vec![], + cev_quads: value, + })); + Ok(()) + } + } + + fn add_extension(&mut self, key: i128, value: ExtensionValue<'a>) -> Result<(), CoservError> { + if let Some(ref mut res) = self.result_set { + match res { + ResultSetTypeChoice::Extensions(ref mut ext) => { + ext.insert(key.into(), value); + Ok(()) + } + other => Err(CoservError::SetQuadsFailed( + "result set extensions".to_string(), + other.to_string(), + )), + } + } else { + let mut extensions = ExtensionMap::default(); + extensions.insert(key.into(), value); + self.result_set = Some(ResultSetTypeChoice::Extensions(extensions)); + Ok(()) + } + } + + pub fn result_set(mut self, value: ResultSetTypeChoice<'a>) -> Self { + self.result_set = Some(value); + self + } + + pub fn expiry(mut self, value: TimeStamp) -> Self { + self.expiry = Some(value); + self + } + + pub fn source_artifacts(mut self, value: Vec) -> Self { + self.source_artifacts = Some(value); + self + } + + pub fn build(self) -> Result, CoservError> { + if self.result_set.is_none() && self.source_artifacts.is_none() { + Err(CoservError::InvalidResult( + "both result-set and source artifacts cannot be empty".into(), + )) + } else { + Ok(CoservResult { + result_set: self.result_set, + expiry: self.expiry.ok_or(CoservError::RequiredFieldNotSet( + "expiry".into(), + "result".into(), + ))?, + source_artifacts: self.source_artifacts, + }) + } + } +} + +/// Result set type: reference values, endorsed values, trust anchors +/// or result set extionsions +#[derive(Debug, PartialEq, Clone, Display)] +pub enum ResultSetTypeChoice<'a> { + #[display("reference values")] + ReferenceValues(ReferenceValuesResult<'a>), + #[display("endorsed values")] + EndorsedValues(EndorsedValuesResult<'a>), + #[display("trust anchors")] + TrustAnchors(TrustAnchorsResult<'a>), + #[display("result set extensions")] + Extensions(ExtensionMap<'a>), +} + +/// Represents reference value quad. +/// Use [ReferenceValuesQuadBuilder] to build this. +pub type ReferenceValuesQuad<'a> = QuadType<'a, ReferenceTripleRecord<'a>>; + +/// Represents endorsed value quad. +/// Use [EndorsedValuesQuadBuilder] to build this. +pub type EndorsedValuesQuad<'a> = QuadType<'a, EndorsedTripleRecord<'a>>; + +/// Represents conditional endorsement quad. +/// Use [ConditionalEndorsementQuadBuilder] to build this. +pub type ConditionalEndorsementQuad<'a> = QuadType<'a, ConditionalEndorsementTripleRecord<'a>>; + +/// Represents attest key quad. +/// Use [AttestKeyQuadBuilder] to build this. +pub type AttestKeyQuad<'a> = QuadType<'a, AttestKeyTripleRecord<'a>>; + +/// Builder for [ReferenceValuesQuad] +pub type ReferenceValuesQuadBuilder<'a> = QuadBuilder<'a, ReferenceTripleRecord<'a>>; + +/// Builder for [EndorsedValuesQuad] +pub type EndorsedValuesQuadBuilder<'a> = QuadBuilder<'a, EndorsedTripleRecord<'a>>; + +/// Builder for [ConditionalEndorsementQuad] +pub type ConditionalEndorsementQuadBuilder<'a> = + QuadBuilder<'a, ConditionalEndorsementTripleRecord<'a>>; + +/// Builder for [AttestKeyQuad] +pub type AttestKeyQuadBuilder<'a> = QuadBuilder<'a, AttestKeyTripleRecord<'a>>; + +// empty trait to bound which triples can +// be used by QuadType +trait AcceptedTriples {} + +impl AcceptedTriples for ReferenceTripleRecord<'_> {} +impl AcceptedTriples for EndorsedTripleRecord<'_> {} +impl AcceptedTriples for ConditionalEndorsementTripleRecord<'_> {} +impl AcceptedTriples for AttestKeyTripleRecord<'_> {} + +/// represents reference-values group from +/// +#[derive(Debug, PartialEq, Clone)] +pub struct ReferenceValuesResult<'a> { + pub rv_quads: Vec>, +} + +/// represents endorsed-values group from +/// +#[derive(Debug, PartialEq, Clone)] +pub struct EndorsedValuesResult<'a> { + pub ev_quads: Vec>, + pub cev_quads: Vec>, +} + +/// represents trust-anchors group from +/// +#[derive(Debug, PartialEq, Clone)] +pub struct TrustAnchorsResult<'a> { + pub ak_quads: Vec>, + //TODO: CoTS +} + +/// Generic type used to define quads. +/// Use [ReferenceValuesQuad], [EndorsedValuesQuad], +/// [ConditionalEndorsementQuad], [AttestKeyQuad] instead +#[allow(private_bounds)] +#[derive(Debug, PartialEq, Clone)] +pub struct QuadType<'a, T> +where + T: Serialize + AcceptedTriples, +{ + pub authorities: Vec>, + pub triple: T, +} + +/// Builder for [QuadType] +/// Use [ReferenceValuesQuadBuilder], [EndorsedValuesQuadBuilder], +/// [ConditionalEndorsementQuadBuilder], [AttestKeyQuadBuilder] instead +#[allow(private_bounds)] +#[derive(Debug)] +pub struct QuadBuilder<'a, T> +where + T: Serialize + AcceptedTriples, +{ + authorities: Option>>, + triple: Option, +} + +impl Default for QuadBuilder<'_, T> +where + T: Serialize + AcceptedTriples, +{ + fn default() -> Self { + QuadBuilder { + authorities: None, + triple: None, + } + } +} + +#[allow(private_bounds)] +impl<'a, T> QuadBuilder<'a, T> +where + T: Serialize + AcceptedTriples, +{ + pub fn new() -> Self { + Self::default() + } + + pub fn triple(mut self, value: T) -> Self { + self.triple = Some(value); + self + } + + pub fn add_authority(&mut self, value: CryptoKeyTypeChoice<'a>) { + if let Some(ref mut au) = self.authorities { + au.push(value); + } else { + self.authorities = Some(vec![value]); + } + } + + pub fn authorities(mut self, value: Vec>) -> Self { + self.authorities = Some(value); + self + } + + pub fn build(self) -> Result, CoservError> { + Ok(QuadType { + authorities: self.authorities.ok_or(CoservError::RequiredFieldNotSet( + "authorities".into(), + "quad".into(), + ))?, + triple: self.triple.ok_or(CoservError::RequiredFieldNotSet( + "triples".into(), + "quad".into(), + ))?, + }) + } +} + +impl Serialize for QuadType<'_, T> +where + T: Serialize + AcceptedTriples, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(2))?; + map.serialize_entry(&1, &self.authorities)?; + map.serialize_entry(&2, &self.triple)?; + map.end() + } +} + +impl<'de, 'a, T> Deserialize<'de> for QuadType<'a, T> +where + T: Serialize + Deserialize<'de> + AcceptedTriples + 'a, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(QuadTypeVisitor::<'a, T> { + marker: PhantomData, + }) + } +} + +struct QuadTypeVisitor<'a, T> { + marker: PhantomData<&'a T>, +} + +impl<'de, 'a, T> Visitor<'de> for QuadTypeVisitor<'a, T> +where + T: Serialize + Deserialize<'de> + AcceptedTriples + 'a, +{ + type Value = QuadType<'a, T>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CoSERV quad type") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut builder = QuadBuilder::new(); + loop { + match access.next_key::()? { + Some(1) => { + builder = + builder.authorities(access.next_value::>>()?) + } + Some(2) => builder = builder.triple(access.next_value::()?), + Some(n) => Err(M::Error::unknown_field(n.to_string().as_str(), &["1", "2"]))?, + None => break, + }; + } + builder.build().map_err(M::Error::custom) + } +} + +// wrapper around cmw::CMW that implements Serialize, Deserialize from serde +#[derive(Debug, PartialEq)] +struct CmwCborRecordType<'a>(Cow<'a, CMW>); + +impl Serialize for CmwCborRecordType<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let cbor = self.0.marshal_cbor().map_err(S::Error::custom)?; + let value = ciborium::from_reader::(cbor.as_slice()) + .map_err(S::Error::custom)?; + value.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for CmwCborRecordType<'_> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = ciborium::Value::deserialize(deserializer)?; + let mut cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut cbor).map_err(D::Error::custom)?; + let record = CMW::unmarshal_cbor(cbor.as_slice()).map_err(D::Error::custom)?; + Ok(CmwCborRecordType(Cow::Owned(record))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::DateTime; + use cmw::monad::Monad; + use cmw::Mime; + use corim_rs::triples::MeasurementValuesMap; + use corim_rs::triples::{ + AttestKeyTripleRecord, ConditionalEndorsementTripleRecord, CryptoKeyTypeChoice, + EndorsedTripleRecord, InstanceIdTypeChoice, MeasurementMap, ReferenceTripleRecord, + }; + use corim_rs::{Bytes, EnvironmentMap, StatefulEnvironmentRecord, TriplesRecordCondition}; + use std::str::FromStr; + + #[test] + fn test_cmw_wrapper() { + let cmw = CMW::Monad( + Monad::new_media_type( + Mime::from_str("application/vnd.example.refvals").unwrap(), + vec![0x01, 0x02, 0x03, 0x04], + None, + ) + .unwrap(), + ); + // need to marshal and unmarshal to set the format field + let cmw_cbor = cmw.marshal_cbor().unwrap(); + let cmw = CMW::unmarshal_cbor(cmw_cbor.as_slice()).unwrap(); + + let tests: Vec<(CmwCborRecordType, Vec)> = + vec![(CmwCborRecordType(Cow::Owned(cmw)), cmw_cbor)]; + for (i, (val, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&val, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {val:?}"); + + let val_de: CmwCborRecordType = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!(*val, val_de, "de at index {i}: {val:?} != {val_de:?}"); + } + } + + #[test] + fn test_reference_values_quad() { + let m1 = MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }; + let tests: Vec<(ReferenceValuesQuad, Vec)> = vec![( + ReferenceValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: ReferenceTripleRecord { + ref_env: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + ref_claims: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }, + vec![ + 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + )]; + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: ReferenceValuesQuad = + ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = + ciborium::from_reader([0xa1, 0x03, 0x80].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_endorsed_values_quad() { + let m1 = MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }; + let tests: Vec<(EndorsedValuesQuad, Vec)> = vec![( + EndorsedValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: EndorsedTripleRecord { + condition: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + endorsement: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }, + vec![ + 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + )]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: EndorsedValuesQuad = + ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + + let err: Result = + ciborium::from_reader([0xa1, 0x03, 0x80].as_slice()); + assert!(err.is_err()); + } + + #[test] + fn test_conditional_endorsement_quad() { + let (m1, m2) = ( + MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }, + MeasurementValuesMap { + name: Some("bar".into()), + ..Default::default() + }, + ); + let tests: Vec<(ConditionalEndorsementQuad, Vec)> = vec![( + ConditionalEndorsementQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: ConditionalEndorsementTripleRecord { + conditions: vec![StatefulEnvironmentRecord { + environment: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + claims_list: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }], + endorsements: vec![EndorsedTripleRecord { + condition: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + endorsement: vec![MeasurementMap { + mkey: None, + mval: m2.clone(), + authorized_by: None, + }], + }], + }, + }, + vec![ + 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x82, // array(2) + 0x81, // + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0x81, 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x62, 0x61, 0x72, // "bar" + ], + )]; + + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: ConditionalEndorsementQuad = + ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + } + + #[test] + fn test_attest_key_quad() { + let tests: Vec<(AttestKeyQuad, Vec)> = vec![( + AttestKeyQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: AttestKeyTripleRecord { + environment: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + key_list: vec![CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )], + conditions: Some(TriplesRecordCondition { + mkey: Some("foo".into()), + authorized_by: None, + }), + }, + }, + vec![ + 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x83, // array(3) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xa1, // map(1) + 0x00, // unsigned(0) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + ], + )]; + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + // Fix corim-rs + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: AttestKeyQuad = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + } + + #[test] + fn test_coserv_result() { + let m1 = MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }; + let cmw = CMW::Monad( + Monad::new_media_type( + Mime::from_str("application/vnd.example.refvals").unwrap(), + vec![0x01, 0x02, 0x03, 0x04], + None, + ) + .unwrap(), + ); + // need to marshal and unmarshal to set the format to cborrecord + let cmw_cbor = cmw.marshal_cbor().unwrap(); + let cmw = CMW::unmarshal_cbor(cmw_cbor.as_slice()).unwrap(); + let tests: Vec<(CoservResult, Vec)> = vec![ + ( + CoservResult { + result_set: Some(ResultSetTypeChoice::ReferenceValues( + ReferenceValuesResult { + rv_quads: vec![ReferenceValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + ), + CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![2, 3, 4, 5]).into(), + ), + ], + triple: ReferenceTripleRecord { + ref_env: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + ref_claims: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }], + }, + )), + expiry: DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + source_artifacts: None, + }, + vec![ + 0xa2, 0x00, 0x81, 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0x0a, 0xc0, 0x74, // tag(0), text(20) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x33, + 0x3a, 0x30, 0x34, 0x3a, 0x33, 0x39, 0x5a, + ], + ), + ( + CoservResult { + result_set: Some(ResultSetTypeChoice::ReferenceValues( + ReferenceValuesResult { + rv_quads: vec![ReferenceValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + ), + CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![2, 3, 4, 5]).into(), + ), + ], + triple: ReferenceTripleRecord { + ref_env: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + ref_claims: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }], + }, + )), + expiry: DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + source_artifacts: Some(vec![cmw]), + }, + vec![ + 0xa3, 0x00, 0x81, 0xa2, // map(2) + 0x01, // unsigned(1) + 0x82, // array(2) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x02, 0x03, 0x04, 0x05, // "\u0002\u0003\u0004\u0005" + 0x02, // unsigned(2) + 0x82, // array(2) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xd9, 0x02, 0x30, // tag(560) + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + 0x81, // array(1) + 0xa1, // map(1) + 0x01, // unsigned(1) + 0xa1, // map(1) + 0x0b, // unsigned(11) + 0x63, // text(3) + 0x66, 0x6f, 0x6f, // "foo" + 0x0a, 0xc0, 0x74, // tag(0), text(20) + 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x54, 0x31, 0x33, + 0x3a, 0x30, 0x34, 0x3a, 0x33, 0x39, 0x5a, 0x0b, 0x81, 0x82, // array(2) + 0x78, 0x1f, // text(31) + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, + 0x6e, 0x64, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x72, 0x65, + 0x66, 0x76, 0x61, 0x6c, 0x73, // "application/vnd.example.refvals" + 0x44, // bytes(4) + 0x01, 0x02, 0x03, 0x04, // "\u0001\u0002\u0003\u0004" + ], + ), + ]; + for (i, (value, expected_cbor)) in tests.iter().enumerate() { + let mut actual_cbor: Vec = vec![]; + ciborium::into_writer(&value, &mut actual_cbor).unwrap(); + assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); + + let value_de: CoservResult = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + assert_eq!( + *value, value_de, + "de at index {i}: {value:?} != {value_de:?}" + ); + } + } + + #[test] + fn test_coserv_result_builder() { + let cmw = CMW::Monad( + Monad::new_media_type( + Mime::from_str("application/vnd.example.refvals").unwrap(), + vec![0x01, 0x02, 0x03, 0x04], + None, + ) + .unwrap(), + ); + + // result set contains only source artifiacts + let builder = CoservResultBuilder::new() + .expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ) + .source_artifacts(vec![cmw.clone()]); + + assert!(builder.build().is_ok()); + + // tests for SetQuadsFailed error + let (m1, m2) = ( + MeasurementValuesMap { + name: Some("foo".into()), + ..Default::default() + }, + MeasurementValuesMap { + name: Some("bar".into()), + ..Default::default() + }, + ); + let rv_quad = ReferenceValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: ReferenceTripleRecord { + ref_env: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + ref_claims: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }; + + let ev_quad = EndorsedValuesQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: EndorsedTripleRecord { + condition: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + endorsement: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }, + }; + + let cev_quad = ConditionalEndorsementQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: ConditionalEndorsementTripleRecord { + conditions: vec![StatefulEnvironmentRecord { + environment: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + claims_list: vec![MeasurementMap { + mkey: None, + mval: m1.clone(), + authorized_by: None, + }], + }], + endorsements: vec![EndorsedTripleRecord { + condition: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + endorsement: vec![MeasurementMap { + mkey: None, + mval: m2.clone(), + authorized_by: None, + }], + }], + }, + }; + + let ak_quad = AttestKeyQuad { + authorities: vec![ + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![1, 2, 3, 4]).into()), + CryptoKeyTypeChoice::Bytes(Bytes::from(vec![2, 3, 4, 5]).into()), + ], + triple: AttestKeyTripleRecord { + environment: EnvironmentMap { + class: None, + instance: Some(InstanceIdTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )), + group: None, + }, + key_list: vec![CryptoKeyTypeChoice::Bytes( + Bytes::from(vec![1, 2, 3, 4]).into(), + )], + conditions: Some(TriplesRecordCondition { + mkey: Some("foo".into()), + authorized_by: None, + }), + }, + }; + + let builder = CoservResultBuilder::new(); + let err = builder.build(); + assert!(err.is_err()); + + let mut builder = CoservResultBuilder::new(); + builder = builder.expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ); + let err = builder.build(); + assert!(err.is_err()); + + let mut builder = CoservResultBuilder::new(); + builder = builder.expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ); + assert!(builder.rv_quads(vec![rv_quad.clone()]).is_ok()); + assert!(builder.rv_quads(vec![rv_quad.clone()]).is_ok()); + assert!(builder.ev_quads(vec![ev_quad.clone()]).is_err()); + assert!(builder.cev_quads(vec![cev_quad.clone()]).is_err()); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_err()); + assert!(builder.build().is_ok()); + + let mut builder = CoservResultBuilder::new(); + builder = builder.expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ); + assert!(builder.ev_quads(vec![ev_quad.clone()]).is_ok()); + assert!(builder.ev_quads(vec![ev_quad.clone()]).is_ok()); + assert!(builder.cev_quads(vec![cev_quad.clone()]).is_ok()); + assert!(builder.cev_quads(vec![cev_quad.clone()]).is_ok()); + assert!(builder.rv_quads(vec![rv_quad.clone()]).is_err()); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_err()); + assert!(builder.build().is_ok()); + + let mut builder = CoservResultBuilder::new(); + builder = builder.expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_ok()); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_ok()); + assert!(builder.ev_quads(vec![ev_quad.clone()]).is_err()); + assert!(builder.cev_quads(vec![cev_quad.clone()]).is_err()); + assert!(builder.rv_quads(vec![rv_quad.clone()]).is_err()); + assert!(builder.build().is_ok()); + + let mut builder = CoservResultBuilder::new(); + builder = builder.expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ); + assert!(builder.add_extension(20, ExtensionValue::Null).is_ok()); + assert!(builder.add_extension(20, ExtensionValue::Null).is_ok()); + assert!(builder.add_extension(21, ExtensionValue::Null).is_ok()); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_err()); + assert!(builder.ak_quads(vec![ak_quad.clone()]).is_err()); + assert!(builder.ev_quads(vec![ev_quad.clone()]).is_err()); + assert!(builder.cev_quads(vec![cev_quad.clone()]).is_err()); + assert!(builder.rv_quads(vec![rv_quad.clone()]).is_err()); + assert!(builder.build().is_ok()); + + // result set contains both source and collected artifacts + let builder = CoservResultBuilder::new() + .expiry( + DateTime::parse_from_rfc3339("2020-09-04T13:04:39Z") + .unwrap() + .into(), + ) + .source_artifacts(vec![cmw.clone()]) + .result_set(ResultSetTypeChoice::ReferenceValues( + ReferenceValuesResult { + rv_quads: vec![rv_quad.clone()], + }, + )); + assert!(builder.build().is_ok()); + } +} diff --git a/src/error/coserv.rs b/src/error/coserv.rs new file mode 100644 index 0000000..5869527 --- /dev/null +++ b/src/error/coserv.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CoservError { + #[error("CoSERV result is invalid because: {0}")] + InvalidResult(String), + #[error("Required field {0} not set in {1}")] + RequiredFieldNotSet(String, String), + #[error("Cannot add {0} to result set of type {1}")] + SetQuadsFailed(String, String), + #[error("CoSERV error: {0}")] + Custom(String), +} + +impl CoservError { + pub fn custom(message: D) -> Self { + Self::Custom(message.to_string()) + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs index d4588bd..945a3bc 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -2,14 +2,18 @@ use thiserror::Error; +mod coserv; mod discovery; +pub use coserv::*; pub use discovery::*; #[derive(Error, Debug)] pub enum Error { #[error("{0}")] Discovery(DiscoveryError), + #[error("{0}")] + Coserv(CoservError), #[error("Unclassified CoSERV error.")] Unknown, } diff --git a/src/lib.rs b/src/lib.rs index 15372a8..b41ee12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,3 +11,6 @@ pub mod error; /// Provides the data model for CoSERV API endpoint and capability discovery pub mod discovery; + +/// Implementation of the CoSERV data model +pub mod coserv; diff --git a/testdata/Makefile b/testdata/Makefile new file mode 100644 index 0000000..67e8c72 --- /dev/null +++ b/testdata/Makefile @@ -0,0 +1,20 @@ +SRCS := $(wildcard *.diag) +CBOR := $(SRCS:.diag=.cbor) +B64U := $(SRCS:.diag=.b64u) +HEX := $(SRCS:.diag=.hex) + +%.cbor: %.diag ; diag2cbor.rb < $< > $@ + +CLEANFILES += $(CBOR) + +%.b64u: %.cbor ; basenc --base64url --wrap 0 $< | tr -d '=' > $@ + +CLEANFILES += $(B64U) + +%.hex: %.cbor ; xxd -p -c0 $< > $@ + +CLEANFILES += $(HEX) + +all: $(CBOR) $(B64U) $(HEX) + +clean: ; $(RM) -f $(CLEANFILES) diff --git a/testdata/example-class-selector-noindent.b64u b/testdata/example-class-selector-noindent.b64u new file mode 100644 index 0000000..6818918 --- /dev/null +++ b/testdata/example-class-selector-noindent.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIKBowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWyBoQDYJVAx-1q_Aj5JkqpOlfnBUDv6AsB0MjAzMC0xMi0wMVQxODozMDowMVoDAA \ No newline at end of file diff --git a/testdata/example-class-selector-noindent.cbor b/testdata/example-class-selector-noindent.cbor new file mode 100644 index 0000000..d600176 Binary files /dev/null and b/testdata/example-class-selector-noindent.cbor differ diff --git a/testdata/example-class-selector-noindent.diag b/testdata/example-class-selector-noindent.diag new file mode 100644 index 0000000..e6e964f --- /dev/null +++ b/testdata/example-class-selector-noindent.diag @@ -0,0 +1 @@ +{0: "tag:example.com,2025:cc-platform#1.0.0", 1: {0: 2, 1: {0: [[{0: 560(h'00112233'), 1: "Example Vendor", 2: "Example Model"}], [{0: 37(h'31fb5abf023e4992aa4e95f9c1503bfa')}]]}, 2: 0("2030-12-01T18:30:01Z"), 3: 0}} \ No newline at end of file diff --git a/testdata/example-class-selector-noindent.hex b/testdata/example-class-selector-noindent.hex new file mode 100644 index 0000000..85d0e4b --- /dev/null +++ b/testdata/example-class-selector-noindent.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008281a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c81a100d8255031fb5abf023e4992aa4e95f9c1503bfa02c074323033302d31322d30315431383a33303a30315a0300 diff --git a/testdata/example-class-selector.b64u b/testdata/example-class-selector.b64u new file mode 100644 index 0000000..6818918 --- /dev/null +++ b/testdata/example-class-selector.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIKBowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWyBoQDYJVAx-1q_Aj5JkqpOlfnBUDv6AsB0MjAzMC0xMi0wMVQxODozMDowMVoDAA \ No newline at end of file diff --git a/testdata/example-class-selector.cbor b/testdata/example-class-selector.cbor new file mode 100644 index 0000000..d600176 Binary files /dev/null and b/testdata/example-class-selector.cbor differ diff --git a/testdata/example-class-selector.diag b/testdata/example-class-selector.diag new file mode 100644 index 0000000..9d2ce12 --- /dev/null +++ b/testdata/example-class-selector.diag @@ -0,0 +1,20 @@ +{ + 0: "tag:example.com,2025:cc-platform#1.0.0", + 1: { + 0: 2, + 1: { + 0: [ + [ { + 0: 560(h'00112233'), + 1: "Example Vendor", + 2: "Example Model" + } ], + [ { + 0: 37(h'31FB5ABF023E4992AA4E95F9C1503BFA') + } ] + ] + }, + 2: 0("2030-12-01T18:30:01Z"), + 3: 0 + } +} diff --git a/testdata/example-class-selector.hex b/testdata/example-class-selector.hex new file mode 100644 index 0000000..85d0e4b --- /dev/null +++ b/testdata/example-class-selector.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008281a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c81a100d8255031fb5abf023e4992aa4e95f9c1503bfa02c074323033302d31322d30315431383a33303a30315a0300 diff --git a/testdata/example-group-selector.b64u b/testdata/example-group-selector.b64u new file mode 100644 index 0000000..77c243a --- /dev/null +++ b/testdata/example-group-selector.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAoKB2QIwRYmZeGVWgdglUDH7Wr8CPkmSqk6V-cFQO_oCwHQyMDMwLTEyLTAxVDE4OjMwOjAxWgMB \ No newline at end of file diff --git a/testdata/example-group-selector.cbor b/testdata/example-group-selector.cbor new file mode 100644 index 0000000..f33511f Binary files /dev/null and b/testdata/example-group-selector.cbor differ diff --git a/testdata/example-group-selector.diag b/testdata/example-group-selector.diag new file mode 100644 index 0000000..11bfee5 --- /dev/null +++ b/testdata/example-group-selector.diag @@ -0,0 +1,14 @@ +{ + 0: "tag:example.com,2025:cc-platform#1.0.0", + 1: { + 0: 2, + 1:{ + 2: [ + [ 560(h'8999786556') ], + [ 37(h'31FB5ABF023E4992AA4E95F9C1503BFA') ] + ] + }, + 2: 0("2030-12-01T18:30:01Z"), + 3: 1 + } +} diff --git a/testdata/example-group-selector.hex b/testdata/example-group-selector.hex new file mode 100644 index 0000000..f4588f8 --- /dev/null +++ b/testdata/example-group-selector.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1028281d9023045899978655681d8255031fb5abf023e4992aa4e95f9c1503bfa02c074323033302d31322d30315431383a33303a30315a0301 diff --git a/testdata/example-instance-selector.b64u b/testdata/example-instance-selector.b64u new file mode 100644 index 0000000..f876b1c --- /dev/null +++ b/testdata/example-instance-selector.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAYKB2QImRwLerb7v3q2B2QIwRYmZeGVWAsB0MjAzMC0xMi0wMVQxODozMDowMVoDAg \ No newline at end of file diff --git a/testdata/example-instance-selector.cbor b/testdata/example-instance-selector.cbor new file mode 100644 index 0000000..7f34832 Binary files /dev/null and b/testdata/example-instance-selector.cbor differ diff --git a/testdata/example-instance-selector.diag b/testdata/example-instance-selector.diag new file mode 100644 index 0000000..8f666a9 --- /dev/null +++ b/testdata/example-instance-selector.diag @@ -0,0 +1,14 @@ +{ + 0: "tag:example.com,2025:cc-platform#1.0.0", + 1: { + 0: 2, + 1: { + 1: [ + [ 550(h'02DEADBEEFDEAD') ], + [ 560(h'8999786556') ] + ] + }, + 2: 0("2030-12-01T18:30:01Z"), + 3: 2 + } +} diff --git a/testdata/example-instance-selector.hex b/testdata/example-instance-selector.hex new file mode 100644 index 0000000..32df8a2 --- /dev/null +++ b/testdata/example-instance-selector.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1018281d902264702deadbeefdead81d9023045899978655602c074323033302d31322d30315431383a33303a30315a0302 diff --git a/testdata/rv-class-simple-results-source-artifacts.b64u b/testdata/rv-class-simple-results-source-artifacts.b64u new file mode 100644 index 0000000..ebfc463 --- /dev/null +++ b/testdata/rv-class-simple-results-source-artifacts.b64u @@ -0,0 +1 @@ +owB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIGBowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWwCwHQyMDMwLTEyLTAxVDE4OjMwOjAxWgMBAqMAgArAdDIwMzAtMTItMTNUMTg6MzA6MDJaC4KCeB9hcHBsaWNhdGlvbi92bmQuZXhhbXBsZS5yZWZ2YWxzRK-urayCeB9hcHBsaWNhdGlvbi92bmQuZXhhbXBsZS5yZWZ2YWxzRK2sq6o \ No newline at end of file diff --git a/testdata/rv-class-simple-results-source-artifacts.cbor b/testdata/rv-class-simple-results-source-artifacts.cbor new file mode 100644 index 0000000..fd01a9f Binary files /dev/null and b/testdata/rv-class-simple-results-source-artifacts.cbor differ diff --git a/testdata/rv-class-simple-results-source-artifacts.diag b/testdata/rv-class-simple-results-source-artifacts.diag new file mode 100644 index 0000000..c312948 --- /dev/null +++ b/testdata/rv-class-simple-results-source-artifacts.diag @@ -0,0 +1,25 @@ +{ + / profile / 0: "tag:example.com,2025:cc-platform#1.0.0", + / query / 1: { + / artifact-type / 0: 2, / reference-values / + / environment-selector / 1: { + / class / 0: [ [ + { + / class-id / 0: 560(h'00112233'), / tagged-bytes / + / vendor / 1: "Example Vendor", + / model / 2: "Example Model" + } + ] ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 1 / source-artifacts / + }, + / results / 2: { + / rvq / 0: [ ], + / expiry / 10: 0("2030-12-13T18:30:02Z"), + / source artifacts / 11: [ + [ "application/vnd.example.refvals", h'afaeadac' ], + [ "application/vnd.example.refvals", h'adacabaa' ] + ] + } +} diff --git a/testdata/rv-class-simple-results-source-artifacts.hex b/testdata/rv-class-simple-results-source-artifacts.hex new file mode 100644 index 0000000..df3d8d8 --- /dev/null +++ b/testdata/rv-class-simple-results-source-artifacts.hex @@ -0,0 +1 @@ +a30078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008181a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c02c074323033302d31322d30315431383a33303a30315a030102a300800ac074323033302d31322d31335431383a33303a30325a0b8282781f6170706c69636174696f6e2f766e642e6578616d706c652e72656676616c7344afaeadac82781f6170706c69636174696f6e2f766e642e6578616d706c652e72656676616c7344adacabaa diff --git a/testdata/rv-class-simple-results.b64u b/testdata/rv-class-simple-results.b64u new file mode 100644 index 0000000..9887179 --- /dev/null +++ b/testdata/rv-class-simple-results.b64u @@ -0,0 +1 @@ +owB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIGBowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWwCwHQyMDMwLTEyLTAxVDE4OjMwOjAxWgMAAqIAgaIBgdkCMEOrze8CgqEAowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWyCoQGiAoKCAUGqggJBuwtrQ29tcG9uZW50IEGhAaICgoIBQcyCAkHdC2tDb21wb25lbnQgQgrAdDIwMzAtMTItMTNUMTg6MzA6MDJa \ No newline at end of file diff --git a/testdata/rv-class-simple-results.cbor b/testdata/rv-class-simple-results.cbor new file mode 100644 index 0000000..2ec2b09 Binary files /dev/null and b/testdata/rv-class-simple-results.cbor differ diff --git a/testdata/rv-class-simple-results.diag b/testdata/rv-class-simple-results.diag new file mode 100644 index 0000000..5d05aa0 --- /dev/null +++ b/testdata/rv-class-simple-results.diag @@ -0,0 +1,54 @@ +{ + / profile / 0: "tag:example.com,2025:cc-platform#1.0.0", + / query / 1: { + / artifact-type / 0: 2, / reference-values / + / environment-selector / 1: { + / class / 0: [ [ + { + / class-id / 0: 560(h'00112233'), / tagged-bytes / + / vendor / 1: "Example Vendor", + / model / 2: "Example Model" + } + ] ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 0 / collected-material / + }, + / results / 2: { + / rvq / 0: [ + { + / authorities / 1: [ 560(h'abcdef') ], + / reference-triple / 2: [ + / environment-map / { + / class / 0: { + / class-id / 0: 560(h'00112233'), + / vendor / 1: "Example Vendor", + / model / 2: "Example Model" + } + }, + [ + / measurement-map / { + / mval / 1: { + / digests / 2: [ + [ 1, h'aa' ], + [ 2, h'bb' ] + ], + / name / 11: "Component A" + } + }, + / measurement-map / { + / mval / 1: { + / digests / 2: [ + [ 1, h'cc' ], + [ 2, h'dd' ] + ], + / name / 11: "Component B" + } + } + ] + ] + } + ], + / expiry / 10: 0("2030-12-13T18:30:02Z") + } +} diff --git a/testdata/rv-class-simple-results.hex b/testdata/rv-class-simple-results.hex new file mode 100644 index 0000000..300c0a8 --- /dev/null +++ b/testdata/rv-class-simple-results.hex @@ -0,0 +1 @@ +a30078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008181a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c02c074323033302d31322d30315431383a33303a30315a030002a20081a20181d9023043abcdef0282a100a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c82a101a20282820141aa820241bb0b6b436f6d706f6e656e742041a101a20282820141cc820241dd0b6b436f6d706f6e656e7420420ac074323033302d31322d31335431383a33303a30325a diff --git a/testdata/rv-class-simple.b64u b/testdata/rv-class-simple.b64u new file mode 100644 index 0000000..009dfb5 --- /dev/null +++ b/testdata/rv-class-simple.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIGBowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWwCwHQyMDMwLTEyLTAxVDE4OjMwOjAxWgMB \ No newline at end of file diff --git a/testdata/rv-class-simple.cbor b/testdata/rv-class-simple.cbor new file mode 100644 index 0000000..c076c62 Binary files /dev/null and b/testdata/rv-class-simple.cbor differ diff --git a/testdata/rv-class-simple.diag b/testdata/rv-class-simple.diag new file mode 100644 index 0000000..35e8b4f --- /dev/null +++ b/testdata/rv-class-simple.diag @@ -0,0 +1,17 @@ +{ + / profile / 0: "tag:example.com,2025:cc-platform#1.0.0", + / query / 1: { + / artifact-type / 0: 2, / reference-values / + / environment-selector / 1: { + / class / 0: [ [ + { + / class-id / 0: 560(h'00112233'), / tagged-bytes / + / vendor / 1: "Example Vendor", + / model / 2: "Example Model" + } + ] ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 1 / source-material / + } +} diff --git a/testdata/rv-class-simple.hex b/testdata/rv-class-simple.hex new file mode 100644 index 0000000..080c493 --- /dev/null +++ b/testdata/rv-class-simple.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008181a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c02c074323033302d31322d30315431383a33303a30315a0301 diff --git a/testdata/rv-class-stateful.b64u b/testdata/rv-class-stateful.b64u new file mode 100644 index 0000000..b53d796 --- /dev/null +++ b/testdata/rv-class-stateful.b64u @@ -0,0 +1 @@ +ogB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIGCowDZAjBEABEiMwFuRXhhbXBsZSBWZW5kb3ICbUV4YW1wbGUgTW9kZWyBoQGiAoGCAUGqC2tDb21wb25lbnQgQQLAdDIwMzAtMTItMDFUMTg6MzA6MDFaAwE \ No newline at end of file diff --git a/testdata/rv-class-stateful.cbor b/testdata/rv-class-stateful.cbor new file mode 100644 index 0000000..5157264 Binary files /dev/null and b/testdata/rv-class-stateful.cbor differ diff --git a/testdata/rv-class-stateful.diag b/testdata/rv-class-stateful.diag new file mode 100644 index 0000000..17dba22 --- /dev/null +++ b/testdata/rv-class-stateful.diag @@ -0,0 +1,29 @@ +{ + / profile / 0: "tag:example.com,2025:cc-platform#1.0.0", + / query / 1: { + / artifact-type / 0: 2, / reference-values / + / environment-selector / 1: { + / stateful-class / 0: [ + [ + / class / { + / class-id / 0: 560(h'00112233'), / tagged-bytes / + / vendor / 1: "Example Vendor", + / model / 2: "Example Model" + }, + / measurements / [ + / measurement-map / { + / mval / 1: { + / digests / 2: [ + [ 1, h'aa' ] + ], + / name / 11: "Component A" + } + } + ] + ] + ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 1 / source-material / + } +} diff --git a/testdata/rv-class-stateful.hex b/testdata/rv-class-stateful.hex new file mode 100644 index 0000000..e3fdfdb --- /dev/null +++ b/testdata/rv-class-stateful.hex @@ -0,0 +1 @@ +a20078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008182a300d902304400112233016e4578616d706c652056656e646f72026d4578616d706c65204d6f64656c81a101a20281820141aa0b6b436f6d706f6e656e74204102c074323033302d31322d30315431383a33303a30315a0301 diff --git a/testdata/rv-results.b64u b/testdata/rv-results.b64u new file mode 100644 index 0000000..9668041 --- /dev/null +++ b/testdata/rv-results.b64u @@ -0,0 +1 @@ +owB4JnRhZzpleGFtcGxlLmNvbSwyMDI1OmNjLXBsYXRmb3JtIzEuMC4wAaQAAgGhAIGBoQDZAjBFiZl4ZVYCwHQyMDMwLTEyLTAxVDE4OjMwOjAxWgMAAqIAgaIBgdkCMEOrze8CgqEAoQDZAjBFiZl4ZVaBogDYJVAx-1q_Aj5JkqpOlfnBUDv6AaIAogBlMS4yLjMBGUAAAdkCKQIKwHQyMDMwLTEyLTEzVDE4OjMwOjAyWg \ No newline at end of file diff --git a/testdata/rv-results.cbor b/testdata/rv-results.cbor new file mode 100644 index 0000000..b1525c5 Binary files /dev/null and b/testdata/rv-results.cbor differ diff --git a/testdata/rv-results.diag b/testdata/rv-results.diag new file mode 100644 index 0000000..151b60b --- /dev/null +++ b/testdata/rv-results.diag @@ -0,0 +1,42 @@ +{ + / profile / 0: "tag:example.com,2025:cc-platform#1.0.0", + / query / 1: { + 0: 2, + 1: { + 0: [ [ + { + 0: 560(h'8999786556') + } + ] ] + }, + 2: 0("2030-12-01T18:30:01Z"), + 3: 0 + }, + / results / 2: { + 0: [ + { + 1: [ 560(h'abcdef') ], + 2: [ + { + 0: { + 0: 560(h'8999786556') + } + }, + [ + { + 0: 37(h'31FB5ABF023E4992AA4E95F9C1503BFA'), + 1: { + / version / 0: { + 0: "1.2.3", + 1: 16384 + }, + / svn / 1: 553(2) + } + } + ] + ] + } + ], + 10: 0("2030-12-13T18:30:02Z") + } +} diff --git a/testdata/rv-results.hex b/testdata/rv-results.hex new file mode 100644 index 0000000..19165b5 --- /dev/null +++ b/testdata/rv-results.hex @@ -0,0 +1 @@ +a30078267461673a6578616d706c652e636f6d2c323032353a63632d706c6174666f726d23312e302e3001a4000201a1008181a100d9023045899978655602c074323033302d31322d30315431383a33303a30315a030002a20081a20181d9023043abcdef0282a100a100d9023045899978655681a200d8255031fb5abf023e4992aa4e95f9c1503bfa01a200a20065312e322e330119400001d90229020ac074323033302d31322d31335431383a33303a30325a