From 06ba3bf61a464f458bdbc784f0b74fa28025ffcf Mon Sep 17 00:00:00 2001 From: Dhanus M Lal Date: Mon, 3 Nov 2025 11:50:39 +0530 Subject: [PATCH 1/6] coserv: implementation of coserv data model This commit adds the implementation of coserv data model, according to https://datatracker.ietf.org/doc/draft-ietf-rats-coserv/02/ Implementation includes unit tests for each module. The coserv testvectors from https://github.com/veraison/corim/ have also been incorporated here for testing. Note: The implementation uses data structures from corim-rs, which do not use fixed length encoding for maps everywhere. CoSERV object should be deterministically encoded, which requires maps and arrays to be fixed lenght. This causes some test cases to fail. This should be fixed by changing maps in corim-rs to fixed length (https://github.com/veraison/corim-rs/issues/44). todo: signing and verification of CoSERV Signed-off-by: Dhanus M Lal --- Cargo.lock | 2010 ++++++++++++++++- Cargo.toml | 5 + src/coserv/common.rs | 118 + src/coserv/mod.rs | 410 ++++ src/coserv/query.rs | 1084 +++++++++ src/coserv/result.rs | 1291 +++++++++++ src/error/coserv.rs | 21 + src/error/mod.rs | 4 + src/lib.rs | 3 + testdata/Makefile | 20 + testdata/example-class-selector-noindent.b64u | 1 + testdata/example-class-selector-noindent.cbor | Bin 0 -> 139 bytes testdata/example-class-selector-noindent.diag | 1 + testdata/example-class-selector-noindent.hex | 1 + testdata/example-class-selector.b64u | 1 + testdata/example-class-selector.cbor | Bin 0 -> 139 bytes testdata/example-class-selector.diag | 20 + testdata/example-class-selector.hex | 1 + testdata/example-group-selector.b64u | 1 + testdata/example-group-selector.cbor | Bin 0 -> 105 bytes testdata/example-group-selector.diag | 14 + testdata/example-group-selector.hex | 1 + testdata/example-instance-selector.b64u | 1 + testdata/example-instance-selector.cbor | Bin 0 -> 97 bytes testdata/example-instance-selector.diag | 14 + testdata/example-instance-selector.hex | 1 + ...class-simple-results-source-artifacts.b64u | 1 + ...class-simple-results-source-artifacts.cbor | Bin 0 -> 224 bytes ...class-simple-results-source-artifacts.diag | 25 + ...-class-simple-results-source-artifacts.hex | 1 + testdata/rv-class-simple-results.b64u | 1 + testdata/rv-class-simple-results.cbor | Bin 0 -> 252 bytes testdata/rv-class-simple-results.diag | 54 + testdata/rv-class-simple-results.hex | 1 + testdata/rv-class-simple.b64u | 1 + testdata/rv-class-simple.cbor | Bin 0 -> 117 bytes testdata/rv-class-simple.diag | 17 + testdata/rv-class-simple.hex | 1 + testdata/rv-class-stateful.b64u | 1 + testdata/rv-class-stateful.cbor | Bin 0 -> 140 bytes testdata/rv-class-stateful.diag | 29 + testdata/rv-class-stateful.hex | 1 + testdata/rv-results.b64u | 1 + testdata/rv-results.cbor | Bin 0 -> 181 bytes testdata/rv-results.diag | 42 + testdata/rv-results.hex | 1 + 46 files changed, 5093 insertions(+), 107 deletions(-) create mode 100644 src/coserv/common.rs create mode 100644 src/coserv/mod.rs create mode 100644 src/coserv/query.rs create mode 100644 src/coserv/result.rs create mode 100644 src/error/coserv.rs create mode 100644 testdata/Makefile create mode 100644 testdata/example-class-selector-noindent.b64u create mode 100644 testdata/example-class-selector-noindent.cbor create mode 100644 testdata/example-class-selector-noindent.diag create mode 100644 testdata/example-class-selector-noindent.hex create mode 100644 testdata/example-class-selector.b64u create mode 100644 testdata/example-class-selector.cbor create mode 100644 testdata/example-class-selector.diag create mode 100644 testdata/example-class-selector.hex create mode 100644 testdata/example-group-selector.b64u create mode 100644 testdata/example-group-selector.cbor create mode 100644 testdata/example-group-selector.diag create mode 100644 testdata/example-group-selector.hex create mode 100644 testdata/example-instance-selector.b64u create mode 100644 testdata/example-instance-selector.cbor create mode 100644 testdata/example-instance-selector.diag create mode 100644 testdata/example-instance-selector.hex create mode 100644 testdata/rv-class-simple-results-source-artifacts.b64u create mode 100644 testdata/rv-class-simple-results-source-artifacts.cbor create mode 100644 testdata/rv-class-simple-results-source-artifacts.diag create mode 100644 testdata/rv-class-simple-results-source-artifacts.hex create mode 100644 testdata/rv-class-simple-results.b64u create mode 100644 testdata/rv-class-simple-results.cbor create mode 100644 testdata/rv-class-simple-results.diag create mode 100644 testdata/rv-class-simple-results.hex create mode 100644 testdata/rv-class-simple.b64u create mode 100644 testdata/rv-class-simple.cbor create mode 100644 testdata/rv-class-simple.diag create mode 100644 testdata/rv-class-simple.hex create mode 100644 testdata/rv-class-stateful.b64u create mode 100644 testdata/rv-class-stateful.cbor create mode 100644 testdata/rv-class-stateful.diag create mode 100644 testdata/rv-class-stateful.hex create mode 100644 testdata/rv-results.b64u create mode 100644 testdata/rv-results.cbor create mode 100644 testdata/rv-results.diag create mode 100644 testdata/rv-results.hex diff --git a/Cargo.lock b/Cargo.lock index 9897658..e88588e 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" @@ -44,14 +127,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half", + "half 2.6.0", +] + +[[package]] +name = "cmw" +version = "0.1.0" +source = "git+https://github.com/veraison/rust-cmw#108b75cfa1e75eada3eaf15d9c124cf6697bd070" +dependencies = [ + "base64 0.22.1", + "base64-serde", + "bitflags 1.3.2", + "hex", + "iri-string", + "lazy_static", + "mime", + "minicbor", + "once_cell", + "regex", + "reqwest", + "serde", + "serde_cbor", + "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#ded07af3a3b237ccc471160c6dce624fb2383f8a" +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 +220,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 +247,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 = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[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-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512ce2c37128698ea15c99b3518936c78a8b112b92468e7b95b9fa045666ebd8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + +[[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..a2ae149 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 = { git = "https://github.com/veraison/rust-cmw" } +base64 = "0.22.1" 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..e6c973f --- /dev/null +++ b/src/coserv/mod.rs @@ -0,0 +1,410 @@ +// 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, Query, QueryBuilder, +//! ResultTypeChoice, StatefulInstance, StatefulInstanceBuilder, +//!}; +//! +//!use coserv_rs::coserv::corim_rs::{InstanceIdTypeChoice, ProfileTypeChoice}; +//! +//!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 = QueryBuilder::new() +//! .artifact_type(ArtifactTypeChoice::ReferenceValues) +//! .result_type(ResultTypeChoice::SourceArtifacts) +//! .environment_selector(EnvironmentSelectorMap::Instance(instances)) +//! .build() +//! .unwrap(); +//! +//! // create coserv map +//! let coserv = CoservBuilder::new() +//! .profile(ProfileTypeChoice::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, 0xD8, 0x20, 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 corim_rs::corim::ProfileTypeChoice; +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; + +pub use cmw; + +/// Represents a CoSERV object +#[derive(Debug, PartialEq)] +pub struct Coserv<'a> { + /// CoSERV profile + pub profile: ProfileTypeChoice<'a>, + /// CoSERV query map + pub query: Query<'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: ProfileTypeChoice<'a>) -> Self { + self.profile = Some(value); + self + } + + /// Set the query + pub fn query(mut self, value: Query<'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, + }) + } +} + +#[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(ProfileTypeChoice::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()); + } +} diff --git a/src/coserv/query.rs b/src/coserv/query.rs new file mode 100644 index 0000000..3dd984b --- /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 [QueryBuilder] to build this struct. +#[derive(Debug, PartialEq)] +pub struct Query<'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 [Query] +#[derive(Debug, Default)] +pub struct QueryBuilder<'a> { + pub artifact_type: Option, + pub environment_selector: Option>, + pub timestamp: Option, + pub result_type: Option, +} + +impl<'a> QueryBuilder<'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(Query { + 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 Query<'_> { + 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 Query<'_> { + 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 = Query<'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 = QueryBuilder::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<(Query, Vec)> = vec![( + Query { + 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: Query = 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..a6409a3 --- /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( + 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( + 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( + 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 0000000000000000000000000000000000000000..d60017685d40e85fabe3b7fd32fb4f2d7a9e804f GIT binary patch literal 139 zcmZ3)P@z_mm~NF?k(gVMld6}TpQ~eJU}S2QoUB`rlUS0LUzDqCsAr&Oz_^5giE$xA zQ{!TWn@k2S41!9=jCrmQZ3#_@zoYgu*?CS{#_@zoYgu*?CS{HSQ#5w85l+}GXelof-4LF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b1525c565df305354c875bfab7477446bc3242ae GIT binary patch literal 181 zcmZ3?P@z_mm~NF?k(gVMld6}TpQ~eJU}S2QoUB`rlUS0LUzDqCsAr&Oz_^5giE$xA zW8*@Gn@k3-oii&^! Date: Thu, 13 Nov 2025 18:20:34 +0530 Subject: [PATCH 2/6] coserv profile: fix: make untagged Apply change suggested by Paul. Changed coserv profile to untagged types. The profile can be untagged URI or untagged OID. Earlier definition used `ProfileTypeChoice' from corim-rs, which had tagged URI (32) and tagged OID (111) as variants. This commit implements `CoservProfile' enum in this crate with untagged variants. Signed-off-by: Dhanus M Lal --- src/coserv/mod.rs | 100 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/src/coserv/mod.rs b/src/coserv/mod.rs index e6c973f..4289f9c 100644 --- a/src/coserv/mod.rs +++ b/src/coserv/mod.rs @@ -22,10 +22,10 @@ //! ```rust //!use coserv_rs::coserv::{ //! ArtifactTypeChoice, Coserv, CoservBuilder, EnvironmentSelectorMap, Query, QueryBuilder, -//! ResultTypeChoice, StatefulInstance, StatefulInstanceBuilder, +//! ResultTypeChoice, StatefulInstance, StatefulInstanceBuilder, CoservProfile, //!}; //! -//!use coserv_rs::coserv::corim_rs::{InstanceIdTypeChoice, ProfileTypeChoice}; +//!use coserv_rs::coserv::corim_rs::InstanceIdTypeChoice; //! //!fn main() { //! // create list of stateful instances @@ -54,7 +54,7 @@ //! //! // create coserv map //! let coserv = CoservBuilder::new() -//! .profile(ProfileTypeChoice::Uri("foo".into())) +//! .profile(CoservProfile::Uri("foo".into())) //! .query(query) //! .build() //! .unwrap(); @@ -83,11 +83,11 @@ //! //!fn main() { //! let cbor_data: Vec = vec![ -//! 0xA2, 0x00, 0xD8, 0x20, 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, +//! 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(); @@ -152,7 +152,6 @@ use crate::error::CoservError; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use corim_rs::corim::ProfileTypeChoice; use serde::{ de::{self, Deserialize, Deserializer, Error, MapAccess, Visitor}, ser::{Serialize, SerializeMap, Serializer}, @@ -177,13 +176,18 @@ 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: ProfileTypeChoice<'a>, + pub profile: CoservProfile, /// CoSERV query map pub query: Query<'a>, /// optional CoSERV result map @@ -221,7 +225,7 @@ impl<'a> Coserv<'a> { /// Builder for CoSERV object #[derive(Debug, Default)] pub struct CoservBuilder<'a> { - pub profile: Option>, + pub profile: Option, pub query: Option>, pub results: Option>, } @@ -232,7 +236,7 @@ impl<'a> CoservBuilder<'a> { } /// Set the profile - pub fn profile(mut self, value: ProfileTypeChoice<'a>) -> Self { + pub fn profile(mut self, value: CoservProfile) -> Self { self.profile = Some(value); self } @@ -305,7 +309,7 @@ impl<'de> Deserialize<'de> for Coserv<'_> { loop { match access.next_key::()? { Some(0) => { - builder = builder.profile(access.next_value::()?); + builder = builder.profile(access.next_value::()?); } Some(1) => { builder = builder.query(access.next_value::()?); @@ -331,6 +335,46 @@ impl<'de> Deserialize<'de> for Coserv<'_> { } } +/// 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::*; @@ -397,7 +441,7 @@ mod tests { assert!(builder.build().is_err()); let mut builder = CoservBuilder::new(); - builder = builder.profile(ProfileTypeChoice::Uri("foo".into())); + builder = builder.profile(CoservProfile::Uri("foo".into())); assert!(builder.build().is_err()); } @@ -407,4 +451,32 @@ mod tests { 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:?}"); + } + } } From 7861874e06c58806ee44de645069fcf6872b241c Mon Sep 17 00:00:00 2001 From: Dhanus M Lal Date: Mon, 1 Dec 2025 10:55:00 +0530 Subject: [PATCH 3/6] use cmw-rs from crates.io Signed-off-by: Dhanus M Lal --- Cargo.lock | 41 +++++++++++++++++++++-------------------- Cargo.toml | 2 +- src/coserv/result.rs | 6 +++--- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e88588e..da48159 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,13 +127,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 2.6.0", + "half", ] [[package]] name = "cmw" version = "0.1.0" -source = "git+https://github.com/veraison/rust-cmw#108b75cfa1e75eada3eaf15d9c124cf6697bd070" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e7b1e30f775e494a051c8fc6d6776caee8e09033554f7844c20571da36cf06" dependencies = [ "base64 0.22.1", "base64-serde", @@ -142,12 +143,12 @@ dependencies = [ "iri-string", "lazy_static", "mime", - "minicbor", + "minicbor 1.1.0", + "minicbor-serde", "once_cell", "regex", "reqwest", "serde", - "serde_cbor", "serde_json", "simple_asn1", "thiserror 2.0.17", @@ -430,12 +431,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" - [[package]] name = "half" version = "2.6.0" @@ -817,6 +812,12 @@ 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" @@ -828,6 +829,16 @@ dependencies = [ "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" @@ -1203,16 +1214,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half 1.8.3", - "serde", -] - [[package]] name = "serde_core" version = "1.0.228" diff --git a/Cargo.toml b/Cargo.toml index a2ae149..36758db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,5 +41,5 @@ mime = "0.3.17" thiserror = "2.0.17" corim-rs = { git = "https://github.com/veraison/corim-rs" } chrono = "0.4.42" -cmw = { git = "https://github.com/veraison/rust-cmw" } +cmw = "0.1.0" base64 = "0.22.1" diff --git a/src/coserv/result.rs b/src/coserv/result.rs index a6409a3..82ee1fb 100644 --- a/src/coserv/result.rs +++ b/src/coserv/result.rs @@ -566,7 +566,7 @@ mod tests { #[test] fn test_cmw_wrapper() { let cmw = CMW::Monad( - Monad::new( + Monad::new_media_type( Mime::from_str("application/vnd.example.refvals").unwrap(), vec![0x01, 0x02, 0x03, 0x04], None, @@ -910,7 +910,7 @@ mod tests { ..Default::default() }; let cmw = CMW::Monad( - Monad::new( + Monad::new_media_type( Mime::from_str("application/vnd.example.refvals").unwrap(), vec![0x01, 0x02, 0x03, 0x04], None, @@ -1072,7 +1072,7 @@ mod tests { #[test] fn test_coserv_result_builder() { let cmw = CMW::Monad( - Monad::new( + Monad::new_media_type( Mime::from_str("application/vnd.example.refvals").unwrap(), vec![0x01, 0x02, 0x03, 0x04], None, From 4c3136642648f750334fa5aee8d790d0b01ff4db Mon Sep 17 00:00:00 2001 From: Dhanus M Lal Date: Mon, 1 Dec 2025 11:32:41 +0530 Subject: [PATCH 4/6] rename: Query -> CoservQuery Signed-off-by: Dhanus M Lal --- src/coserv/mod.rs | 12 ++++++------ src/coserv/query.rs | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/coserv/mod.rs b/src/coserv/mod.rs index 4289f9c..599efec 100644 --- a/src/coserv/mod.rs +++ b/src/coserv/mod.rs @@ -21,7 +21,7 @@ //! //! ```rust //!use coserv_rs::coserv::{ -//! ArtifactTypeChoice, Coserv, CoservBuilder, EnvironmentSelectorMap, Query, QueryBuilder, +//! ArtifactTypeChoice, Coserv, CoservBuilder, EnvironmentSelectorMap, CoservQuery, CoservQueryBuilder, //! ResultTypeChoice, StatefulInstance, StatefulInstanceBuilder, CoservProfile, //!}; //! @@ -45,7 +45,7 @@ //! ]; //! //! // create query map -//! let query = QueryBuilder::new() +//! let query = CoservQueryBuilder::new() //! .artifact_type(ArtifactTypeChoice::ReferenceValues) //! .result_type(ResultTypeChoice::SourceArtifacts) //! .environment_selector(EnvironmentSelectorMap::Instance(instances)) @@ -189,7 +189,7 @@ pub struct Coserv<'a> { /// CoSERV profile pub profile: CoservProfile, /// CoSERV query map - pub query: Query<'a>, + pub query: CoservQuery<'a>, /// optional CoSERV result map pub results: Option>, } @@ -226,7 +226,7 @@ impl<'a> Coserv<'a> { #[derive(Debug, Default)] pub struct CoservBuilder<'a> { pub profile: Option, - pub query: Option>, + pub query: Option>, pub results: Option>, } @@ -242,7 +242,7 @@ impl<'a> CoservBuilder<'a> { } /// Set the query - pub fn query(mut self, value: Query<'a>) -> Self { + pub fn query(mut self, value: CoservQuery<'a>) -> Self { self.query = Some(value); self } @@ -312,7 +312,7 @@ impl<'de> Deserialize<'de> for Coserv<'_> { builder = builder.profile(access.next_value::()?); } Some(1) => { - builder = builder.query(access.next_value::()?); + builder = builder.query(access.next_value::()?); } Some(2) => { builder = builder.results(access.next_value::()?); diff --git a/src/coserv/query.rs b/src/coserv/query.rs index 3dd984b..66b228a 100644 --- a/src/coserv/query.rs +++ b/src/coserv/query.rs @@ -11,9 +11,9 @@ use std::fmt; use std::marker::PhantomData; /// Representation of CoSERV query map. -/// Use [QueryBuilder] to build this struct. +/// Use [CoservQueryBuilder] to build this struct. #[derive(Debug, PartialEq)] -pub struct Query<'a> { +pub struct CoservQuery<'a> { /// Query artifact type pub artifact_type: ArtifactTypeChoice, /// environment selector map @@ -24,16 +24,16 @@ pub struct Query<'a> { pub result_type: ResultTypeChoice, } -/// Builder for [Query] +/// Builder for [CoservQuery] #[derive(Debug, Default)] -pub struct QueryBuilder<'a> { +pub struct CoservQueryBuilder<'a> { pub artifact_type: Option, pub environment_selector: Option>, pub timestamp: Option, pub result_type: Option, } -impl<'a> QueryBuilder<'a> { +impl<'a> CoservQueryBuilder<'a> { pub fn new() -> Self { Self::default() } @@ -60,8 +60,8 @@ impl<'a> QueryBuilder<'a> { self } - pub fn build(self) -> Result, CoservError> { - Ok(Query { + pub fn build(self) -> Result, CoservError> { + Ok(CoservQuery { artifact_type: self.artifact_type.ok_or(CoservError::RequiredFieldNotSet( "artifact type".into(), "query".into(), @@ -78,7 +78,7 @@ impl<'a> QueryBuilder<'a> { } } -impl Serialize for Query<'_> { +impl Serialize for CoservQuery<'_> { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -92,7 +92,7 @@ impl Serialize for Query<'_> { } } -impl<'de> Deserialize<'de> for Query<'_> { +impl<'de> Deserialize<'de> for CoservQuery<'_> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -101,7 +101,7 @@ impl<'de> Deserialize<'de> for Query<'_> { marker: PhantomData<&'a str>, } impl<'de, 'a> Visitor<'de> for QueryVisitor<'a> { - type Value = Query<'a>; + type Value = CoservQuery<'a>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "map containing CoSERV query (Query)") @@ -111,7 +111,7 @@ impl<'de> Deserialize<'de> for Query<'_> { where M: MapAccess<'de>, { - let mut builder = QueryBuilder::new(); + let mut builder = CoservQueryBuilder::new(); loop { match access.next_key::()? { Some(0) => { @@ -982,8 +982,8 @@ mod tests { name: Some("foo".into()), ..Default::default() }; - let tests: Vec<(Query, Vec)> = vec![( - Query { + let tests: Vec<(CoservQuery, Vec)> = vec![( + CoservQuery { artifact_type: ArtifactTypeChoice::ReferenceValues, environment_selector: EnvironmentSelectorMap::Group(vec![ StatefulGroup { @@ -1041,7 +1041,7 @@ mod tests { ciborium::into_writer(&value, &mut actual_cbor).unwrap(); assert_eq!(*expected_cbor, actual_cbor, "ser at index {i}: {value:?}"); - let value_de: Query = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); + let value_de: CoservQuery = ciborium::from_reader(actual_cbor.as_slice()).unwrap(); assert_eq!( *value, value_de, "de at index {i}: {value:?} != {value_de:?}" @@ -1049,7 +1049,7 @@ mod tests { } let cbor_invalid_key: Vec = vec![0xa1, 0x04, 0x80]; - let err: Result = ciborium::from_reader(cbor_invalid_key.as_slice()); + let err: Result = ciborium::from_reader(cbor_invalid_key.as_slice()); assert!(err.is_err()); let cbor_missing_timestamp: Vec = vec![ @@ -1078,7 +1078,7 @@ mod tests { 0x03, // unsigned(3) 0x00, // unsigned(0) ]; - let err: Result = ciborium::from_reader(cbor_missing_timestamp.as_slice()); + let err: Result = ciborium::from_reader(cbor_missing_timestamp.as_slice()); assert!(err.is_err()); } } From 06aefbbea461f7b19917046a292f6fa51c313c8b Mon Sep 17 00:00:00 2001 From: Dhanus M Lal Date: Wed, 21 Jan 2026 15:38:01 +0530 Subject: [PATCH 5/6] use latest corim-rs Signed-off-by: Dhanus M Lal --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index da48159..86e2401 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "corim-rs" version = "0.1.0" -source = "git+https://github.com/veraison/corim-rs#ded07af3a3b237ccc471160c6dce624fb2383f8a" +source = "git+https://github.com/veraison/corim-rs#4d868da478ded7f99ae9b822eeb63bdb5cfcd04c" dependencies = [ "base64 0.22.1", "ciborium", From 5914e287cf03d6fadb3d1b5c347cc7683868b9e4 Mon Sep 17 00:00:00 2001 From: Dhanus M Lal Date: Mon, 3 Nov 2025 11:50:39 +0530 Subject: [PATCH 6/6] Implementation of coserv data model This commit adds the implementation of coserv data model, according to https://datatracker.ietf.org/doc/draft-ietf-rats-coserv/02/ Implementation includes unit tests for each module. The coserv testvectors from https://github.com/veraison/corim/ have also been added here for testing. todo: signing and verification of CoSERV Signed-off-by: Dhanus M Lal --- deny.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 = [""]