diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..336e972 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "cargo" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/on-push.yaml b/.github/workflows/on-push.yaml new file mode 100644 index 0000000..82eda72 --- /dev/null +++ b/.github/workflows/on-push.yaml @@ -0,0 +1,18 @@ +name: on-push + +on: + push: + branches: + - main + pull_request_target: + types: + - opened + - synchronize + +jobs: + rust-workflow: + uses: affinidi/pipeline-rust/.github/workflows/on-push.yaml@main + secrets: inherit + with: + auditIgnore: "RUSTSEC-2022-0040,RUSTSEC-2023-0071" + publish_dry_run: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f92c03d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Affinidi Trust Network - Affinidi Trusted Messaging + +## Changelog history + +### 9th September 2024 (release 0.1.5) + +* Renaming crate names +* Setting publich to true for crates.io +* Bumping crate versions + +### 5th September 2024 (release 0.1.4) + +* Updated crates +* did-peer added missing types and support for peer implementation type 0 (supports 0 and 2). + +### 3rd September 2024 (release 0.1.3) + +* Added Debug trait to ClientConfig so we can print the config elsewhere + +### 2nd September 2024 (release: 0.1.2) + +* tokio crate updated +* release version changed to 0.1.2 +* benchmark example - warnings removed diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b704e77 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6823 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "abnf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "087113bd50d9adce24850eed5d0476c7d199d532fce8fab5173650331e09033a" +dependencies = [ + "abnf-core", + "nom", +] + +[[package]] +name = "abnf-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c44e09c43ae1c368fb91a03a566472d0087c26cf7e1b9e8e289c14ede681dd7d" +dependencies = [ + "nom", +] + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "affinidi-did-resolver-cache-sdk" +version = "0.1.5" +dependencies = [ + "blake2", + "clap 4.5.17", + "did-peer", + "futures-util", + "moka", + "num-format", + "number_prefix", + "rand", + "rayon", + "serde", + "serde_json", + "ssi", + "thiserror", + "tokio", + "tokio-tungstenite 0.23.1", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "affinidi-did-resolver-cache-server" +version = "0.1.5" +dependencies = [ + "affinidi-did-resolver-cache-sdk", + "axum", + "axum-server", + "blake2", + "chrono", + "did-peer", + "http 1.1.0", + "moka", + "rand", + "regex", + "serde", + "serde_json", + "ssi", + "thiserror", + "tokio", + "toml 0.8.19", + "tower-http", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "affinidi-did-resolver-methods" +version = "0.1.5" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayref" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "askar-crypto" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39746932b19e345196a089e61a0f0175fc4d673db4b624424d8babf505e48a3d" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "base64 0.21.7", + "blake2", + "block-modes", + "bls12_381", + "cbc", + "chacha20", + "chacha20poly1305", + "cipher", + "crypto_box", + "curve25519-dalek", + "digest 0.10.7", + "ed25519-dalek", + "elliptic-curve", + "group 0.13.0", + "hkdf", + "hmac", + "k256", + "p256", + "p384", + "rand", + "serde", + "serde-json-core", + "sha2 0.10.8", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "aws-lc-rs" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5055edc4a9a1b2a917a818258cdfb86a535947feebd9981adc99667a062c6f85" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper 1.0.1", + "tokio", + "tokio-tungstenite 0.21.0", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-server" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" +dependencies = [ + "arc-swap", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "rustls 0.23.12", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty 2.0.0", + "radium 0.7.0", + "tap", + "wyz 0.5.1", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", +] + +[[package]] +name = "blake3" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "cc", + "cfg-if", + "constant_time_eq 0.3.1", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-modes" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2211b0817f061502a8dd9f11a37e879e79763e3c698d2418cf824d8cb2f21e" + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" +dependencies = [ + "ff 0.13.0", + "group 0.13.0", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "bls12_381_plus" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa37cf2a8c96054d2dc3d708efe35cc0347014af0d30b86c736b4388ff8491c" +dependencies = [ + "arrayref", + "elliptic-curve", + "ff 0.13.0", + "group 0.13.0", + "hex", + "pairing", + "rand_core", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + +[[package]] +name = "btree-range-map" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33" +dependencies = [ + "btree-slab", + "cc-traits", + "range-traits", + "serde", + "slab", +] + +[[package]] +name = "btree-slab" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c" +dependencies = [ + "cc-traits", + "slab", + "smallvec", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cached" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d" +dependencies = [ + "hashbrown 0.11.2", + "once_cell", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-license" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653542a7f5db653bf79ee4b6455c23f8e6b8a9c38c6310fbe14528728c14bd19" +dependencies = [ + "ansi_term", + "anyhow", + "atty", + "cargo_metadata", + "clap 3.2.25", + "csv", + "getopts", + "semver", + "serde", + "serde_derive", + "serde_json", + "toml 0.5.11", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cc-traits" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5" +dependencies = [ + "slab", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half 2.4.1", +] + +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase 0.9.1", + "multihash", + "serde", + "serde_bytes", + "unsigned-varint", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive 3.2.25", + "clap_lex 0.2.4", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive 4.5.13", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.7.2", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "clear_on_drop" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" +dependencies = [ + "cc", +] + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "combination" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "337cdbf3f1a0e643b4a7d1a2ffa39d22342fb6ee25739b5cfb997c28b3586422" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "contextual" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ca71f324d19e85a2e976be04b5ecbb193253794a75adfe2e5044c8bef03f6a" + +[[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 = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "crypto_box" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16182b4f39a82ec8a6851155cc4c0cda3065bb1db33651726a29e1951de0f009" +dependencies = [ + "aead", + "crypto_secretbox", + "curve25519-dalek", + "salsa20", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto_secretbox" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" +dependencies = [ + "aead", + "cipher", + "generic-array", + "poly1305", + "salsa20", + "subtle", + "zeroize", +] + +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.77", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core 0.20.10", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "data-encoding-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "decoded-char" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5440d1dc8ea7cae44cda3c64568db29bfa2434aba51ae66a50c00488841a65a3" + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", + "crypto-bigint 0.3.2", + "pem-rfc7468 0.3.1", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "did-ethr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0417aae3db3cdf06f9d13002337e9d148fa89e8361c08396b3373476c6ab0d3" +dependencies = [ + "hex", + "iref", + "serde_json", + "ssi-caips", + "ssi-dids-core", + "ssi-jwk", + "static-iref", + "thiserror", +] + +[[package]] +name = "did-ion" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02db43d4a30864120023d8e65bec1a762b51efa2e17337c7e1553d83ba96a3b" +dependencies = [ + "base64 0.12.3", + "iref", + "json-patch", + "reqwest 0.11.27", + "serde", + "serde_jcs", + "serde_json", + "sha2 0.10.8", + "ssi-dids-core", + "ssi-jwk", + "ssi-jws", + "ssi-jwt", + "ssi-verification-methods", + "thiserror", +] + +[[package]] +name = "did-jwk" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defbdf936331b247c070cbc77a29470343bc1e76be09f4f78a9e6615964b697e" +dependencies = [ + "async-trait", + "iref", + "multibase 0.8.0", + "serde_jcs", + "serde_json", + "ssi-crypto", + "ssi-dids-core", + "ssi-jwk", + "ssi-verification-methods", + "static-iref", + "thiserror", +] + +[[package]] +name = "did-method-key" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b380da1739823f33e0d1df47f9c4b04ffa3746e42cdfbf2bf15a296eeac3c7d" +dependencies = [ + "bs58", + "iref", + "k256", + "multibase 0.9.1", + "p256", + "serde_json", + "simple_asn1", + "ssi-dids-core", + "ssi-json-ld", + "ssi-jwk", + "ssi-multicodec", + "static-iref", + "thiserror", +] + +[[package]] +name = "did-peer" +version = "0.1.5" +dependencies = [ + "askar-crypto", + "base64 0.22.1", + "iref", + "multibase 0.9.1", + "serde", + "serde-wasm-bindgen", + "serde_json", + "ssi", + "thiserror", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "did-pkh" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7032b64c01971dbd6db7bf13d00364221795524a950c46637e0fb7d0483dcffa" +dependencies = [ + "async-trait", + "bech32", + "bs58", + "chrono", + "iref", + "serde", + "serde_json", + "ssi-caips", + "ssi-crypto", + "ssi-dids-core", + "ssi-jwk", + "static-iref", + "thiserror", +] + +[[package]] +name = "did-tz" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c04e7f709c8d7e26323ac898ed4e9aa2a2628fe8c2223556ffeec01eeb42870b" +dependencies = [ + "async-trait", + "bs58", + "chrono", + "iref", + "json-patch", + "reqwest 0.11.27", + "serde", + "serde_json", + "ssi-core", + "ssi-dids-core", + "ssi-jwk", + "ssi-jws", + "static-iref", + "thiserror", + "url", +] + +[[package]] +name = "did-web" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf0ab6bab0b86cdadda93e2a60d6f231e76199536810be43fd61e22bf0e7e9" +dependencies = [ + "http 0.2.12", + "reqwest 0.11.27", + "ssi-dids-core", + "thiserror", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common", + "subtle", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.9", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki 0.7.3", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize 3.1.15", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize 4.3.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "hkdf", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "ff" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec 1.0.1", + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" +dependencies = [ + "byteorder", + "ff 0.10.1", + "rand_core", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.5.0", + "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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex_fmt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.30", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.12", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.30", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "iref" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374372d9ca7331cec26f307b12552554849143e6b2077be3553576aa9aa8258c" +dependencies = [ + "iref-core", +] + +[[package]] +name = "iref-core" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10559a0d518effd4f2cee107f40f83acf8583dcd3e6760b9b60293b0d2c2a70" +dependencies = [ + "base64 0.22.1", + "pct-str", + "serde", + "smallvec", + "static-regular-grammar", + "thiserror", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-ld" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5e03a47a0118ce03ca3a843d1cc1d6d89c91c0b035dab53ed57ad07068b4a3" +dependencies = [ + "contextual", + "futures", + "iref", + "json-ld-compaction", + "json-ld-context-processing", + "json-ld-core", + "json-ld-expansion", + "json-ld-serialization", + "json-ld-syntax", + "json-syntax", + "locspan", + "rdf-types", + "thiserror", +] + +[[package]] +name = "json-ld-compaction" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fec236664e058a0a43e46ad28bbf00858a7cede40ad17ffdced395a9628a928" +dependencies = [ + "contextual", + "educe 0.4.23", + "futures", + "indexmap 2.5.0", + "iref", + "json-ld-context-processing", + "json-ld-core", + "json-ld-expansion", + "json-ld-syntax", + "json-syntax", + "langtag", + "mown", + "rdf-types", + "thiserror", +] + +[[package]] +name = "json-ld-context-processing" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aaf38d5a714ed573340f921a67c212935335e055ff2bb785337d38f6be282a4" +dependencies = [ + "contextual", + "futures", + "iref", + "json-ld-core", + "json-ld-syntax", + "mown", + "owning_ref", + "rdf-types", + "thiserror", +] + +[[package]] +name = "json-ld-core" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a82b473e2951a3cc3fed5061eb62dfbe3f984bdfb9e918214ad571674f09c8b" +dependencies = [ + "contextual", + "educe 0.4.23", + "futures", + "hashbrown 0.13.2", + "indexmap 2.5.0", + "iref", + "json-ld-syntax", + "json-syntax", + "langtag", + "linked-data", + "log", + "mime", + "once_cell", + "permutohedron", + "pretty_dtoa", + "rdf-types", + "ryu-js", + "serde", + "smallvec", + "static-iref", + "thiserror", +] + +[[package]] +name = "json-ld-expansion" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cf1f43e51f7360a889f83b2ccb075d21b8229d325f3c80764fb0bce49f1ce" +dependencies = [ + "contextual", + "educe 0.4.23", + "futures", + "indexmap 2.5.0", + "iref", + "json-ld-context-processing", + "json-ld-core", + "json-ld-syntax", + "json-syntax", + "langtag", + "mown", + "rdf-types", + "thiserror", +] + +[[package]] +name = "json-ld-serialization" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe6e8a89c6aff8c955a6b9128be4da54f1d64b9564bcd647995610c657e2a4e" +dependencies = [ + "indexmap 2.5.0", + "iref", + "json-ld-core", + "json-syntax", + "linked-data", + "rdf-types", + "thiserror", + "xsd-types", +] + +[[package]] +name = "json-ld-syntax" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a52919be632986e14dd1dbcfe12ce4573db02bf7044a09e7ec882a1ec2af7e6" +dependencies = [ + "contextual", + "decoded-char", + "educe 0.4.23", + "hashbrown 0.13.2", + "indexmap 2.5.0", + "iref", + "json-syntax", + "langtag", + "locspan", + "rdf-types", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "json-number" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c54d19ae7e6fc83aafa649707655a9a0ac956a0f62793bde4cfd193b0693fdf" +dependencies = [ + "lexical", + "ryu-js", + "serde", + "smallvec", +] + +[[package]] +name = "json-patch" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" +dependencies = [ + "serde", + "serde_json", + "treediff", +] + +[[package]] +name = "json-syntax" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "044a68aba3f96d712f492b72be25e10f96201eaaca3207a7d6e68d6d5105fda9" +dependencies = [ + "contextual", + "decoded-char", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "json-number", + "locspan", + "locspan-derive", + "ryu-js", + "serde", + "smallstr", + "smallvec", + "utf8-decode", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0386ec98c26dd721aaa3412bf3a817156ff3ee7cb6959503f8d1095f4ccc51" +dependencies = [ + "primitive-types", + "tiny-keccak", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "langtag" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600" +dependencies = [ + "static-regular-grammar", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lexical" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" +dependencies = [ + "lexical-core", +] + +[[package]] +name = "lexical-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libipld" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9c3aa309c260aa2f174bac968901eddc546e9d85950c28eae6a7bec402f926" +dependencies = [ + "async-trait", + "cached", + "fnv", + "libipld-cbor", + "libipld-cbor-derive", + "libipld-core", + "libipld-json", + "libipld-macro", + "log", + "multihash", + "parking_lot", + "thiserror", +] + +[[package]] +name = "libipld-cbor" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd1ab68c9d26f20c7d0dfea6eecbae8c00359875210001b33ca27d4a02f3d09" +dependencies = [ + "byteorder", + "libipld-core", + "thiserror", +] + +[[package]] +name = "libipld-cbor-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ec2f49393a1347a2d95ebcb248ff75d0d47235919b678036c010a8cd927375" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "libipld-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" +dependencies = [ + "anyhow", + "cid", + "core2", + "multibase 0.9.1", + "multihash", + "serde", + "thiserror", +] + +[[package]] +name = "libipld-json" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18aa481a87f084d98473dd9ece253a9569c762b75f6bbba8217d54e48c9d63b3" +dependencies = [ + "libipld-core", + "multihash", + "serde", + "serde_json", +] + +[[package]] +name = "libipld-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852c011562ae5059b67c3a917f9f5945af5a68df8e39ede4444fff33274d25e2" +dependencies = [ + "libipld-core", +] + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linked-data" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fd672ff31f4a6007acbdd33e1b65e1144081ec09b4189b3d20da3997603e90" +dependencies = [ + "educe 0.4.23", + "im", + "iref", + "json-syntax", + "linked-data-derive", + "rdf-types", + "serde", + "static-iref", + "thiserror", + "xsd-types", +] + +[[package]] +name = "linked-data-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887a1903665b47c2dcff59682d8de1be46813819ae0337d8eaa885035762fbee" +dependencies = [ + "iref", + "proc-macro-error", + "proc-macro2", + "quote", + "static-iref", + "syn 2.0.77", + "thiserror", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "locspan" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33890449fcfac88e94352092944bf321f55e5deb4e289a6f51c87c55731200a0" +dependencies = [ + "serde", +] + +[[package]] +name = "locspan-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88991223b049a3d29ca1f60c05639581336a0f3ee4bf8a659dddecc11c4961a" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "moka" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +dependencies = [ + "async-lock", + "async-trait", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "event-listener 5.3.1", + "futures-util", + "once_cell", + "parking_lot", + "quanta", + "rustc_version", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", +] + +[[package]] +name = "mown" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7627d8bbeb17edbf1c3f74b21488e4af680040da89713b4217d0010e9cbd97e" + +[[package]] +name = "multibase" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b78c60039650ff12e140ae867ef5299a58e19dded4d334c849dc7177083667e2" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake2b_simd 1.0.2", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.7", + "multihash-derive", + "serde", + "serde-big-array", + "sha2 0.10.8", + "sha3", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[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-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.6", + "itoa", +] + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.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 2.0.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.3.2+3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" +dependencies = [ + "num-traits", + "rand", + "serde", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group 0.13.0", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pct-str" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf1bdcc492c285a50bed60860dfa00b50baf1f60c73c7d6b435b01a2a11fd6ff" +dependencies = [ + "thiserror", + "utf8-decode", +] + +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "permutohedron" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" +dependencies = [ + "der 0.5.1", + "pkcs8 0.8.0", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.9", + "spki 0.7.3", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_dtoa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a239bcdfda2c685fda1add3b4695c06225f50075e3cfb5b954e91545587edff2" +dependencies = [ + "ryu_floating_decimal", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e" +dependencies = [ + "fixed-hash", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml 0.5.11", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quanta" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "range-traits" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" + +[[package]] +name = "raw-btree" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9a77e61cd9f37af08f952c1c072081563e129c0949ac4ede30f9e8b6b4b74f" + +[[package]] +name = "raw-cpuid" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdf-types" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8d685353ee1b343c708b6be7751d98a7df8e1d0caaeee67c754318854c01d5" +dependencies = [ + "contextual", + "educe 0.5.11", + "indexmap 2.5.0", + "iref", + "langtag", + "raw-btree", + "replace_with", + "slab", + "static-iref", + "thiserror", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "replace_with" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-rustls 0.24.2", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls 0.27.3", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.3", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "rsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" +dependencies = [ + "byteorder", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8 0.8.0", + "rand_core", + "smallvec", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.7", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + +[[package]] +name = "ryu_floating_decimal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "700de91d5fd6091442d00fdd9ee790af6d4f0f480562b0f5a1e8f59e90aafe73" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der 0.7.9", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9e1ab533c0bc414c34920ec7e5f097101d126ed5eac1a1aac711222e0bbb33" +dependencies = [ + "ryu", + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[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_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_jcs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cacecf649bc1a7c5f0e299cc813977c6a78116abda2b93b1ee01735b71ead9a8" +dependencies = [ + "ryu-js", + "serde", + "serde_json", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[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 = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "base64 0.13.1", + "serde", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap 1.9.3", + "serde", + "serde_json", + "serde_with_macros 2.3.3", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "simple_asn1" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", + "thiserror", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b1aefdf380735ff8ded0b15f31aab05daf1f70216c01c02a12926badd1df9d" +dependencies = [ + "serde", + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.9", +] + +[[package]] +name = "sshkeys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45287473d24bf7ad9ebad1aff097ad0424c16cd9430549170c3a67c5b05705bd" +dependencies = [ + "base64 0.22.1", + "byteorder", + "sha2 0.10.8", +] + +[[package]] +name = "ssi" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8889299d60ee48b92907821a37eb93254aa41f704144403670f2f9c4344ec42e" +dependencies = [ + "document-features", + "ssi-caips", + "ssi-claims", + "ssi-core", + "ssi-crypto", + "ssi-dids", + "ssi-eip712", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-multicodec", + "ssi-rdf", + "ssi-security", + "ssi-ssh", + "ssi-status", + "ssi-ucan", + "ssi-verification-methods", + "ssi-zcap-ld", + "xsd-types", +] + +[[package]] +name = "ssi-bbs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbca03cf1d1cb0253f58cd47e843bdf2ad3626dc2a4a1a831684945507c1a" +dependencies = [ + "rand", + "ssi-claims-core", + "ssi-crypto", + "zkryptium", +] + +[[package]] +name = "ssi-caips" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01098e02eb5601a1bca45ce4d78d659505350effdc7a5df7bff860182bd78a85" +dependencies = [ + "bs58", + "linked-data", + "serde", + "ssi-jwk", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-claims" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95e16edaf82196adec53c4fa06a401c84e332623b19b8b5bfc74b327d135eee" +dependencies = [ + "educe 0.4.23", + "iref", + "json-syntax", + "linked-data", + "locspan", + "pin-project", + "rdf-types", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-data-integrity", + "ssi-dids-core", + "ssi-eip712", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-jwt", + "ssi-sd-jwt", + "ssi-security", + "ssi-vc", + "ssi-vc-jose-cose", + "ssi-verification-methods", + "thiserror", +] + +[[package]] +name = "ssi-claims-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca10ead311b434b7ec0055575fe839d7c7c20fb62fafbf05e989b9572795909" +dependencies = [ + "chrono", + "educe 0.4.23", + "serde", + "ssi-core", + "ssi-crypto", + "ssi-eip712", + "ssi-json-ld", + "thiserror", +] + +[[package]] +name = "ssi-contexts" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728dde2f51db62c4667686139f2958b59ab920f8d9d8531b59fc5a01ef0a3896" + +[[package]] +name = "ssi-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a9bcf4043b59fdb6990c9fb34e6e613f457d0c1341f8c017a9feb3082365e8" +dependencies = [ + "async-trait", + "pin-project", + "serde", + "thiserror", +] + +[[package]] +name = "ssi-crypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7bc78e7fc34a598ce6a1f0e4693db6b936512a7e7ff6e9f12231546e95a3408" +dependencies = [ + "async-trait", + "bs58", + "digest 0.9.0", + "getrandom", + "hex", + "iref", + "k256", + "keccak-hash", + "pin-project", + "ripemd160", + "serde", + "sha2 0.10.8", + "static-iref", + "thiserror", + "zeroize", +] + +[[package]] +name = "ssi-data-integrity" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c01440ec2362473c57d554166543efbe907df1dcbe6249b350d303d6d3d3f2" +dependencies = [ + "chrono", + "iref", + "json-syntax", + "linked-data", + "rdf-types", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-data-integrity-core", + "ssi-data-integrity-suites", + "ssi-di-sd-primitives", + "ssi-eip712", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-rdf", + "ssi-security", + "ssi-verification-methods", + "thiserror", +] + +[[package]] +name = "ssi-data-integrity-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b041f6a250551a018e3efabf5c404c4f5d6682edcabd0e6b930baf7a3d3c94b6" +dependencies = [ + "chrono", + "contextual", + "derivative", + "digest 0.10.7", + "educe 0.4.23", + "futures", + "hex", + "iref", + "json-syntax", + "linked-data", + "locspan", + "multibase 0.9.1", + "rdf-types", + "self_cell", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-rdf", + "ssi-security", + "ssi-verification-methods", + "static-iref", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-data-integrity-suites" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deab6070a99ecaff8a3a4168859582bb52d4279633cecd5788b9858a27f041aa" +dependencies = [ + "async-trait", + "base64 0.12.3", + "chrono", + "contextual", + "derivative", + "educe 0.4.23", + "futures", + "getrandom", + "hex", + "iref", + "json-syntax", + "k256", + "lazy_static", + "linked-data", + "locspan", + "multibase 0.9.1", + "p256", + "pin-project", + "rand", + "rdf-types", + "self_cell", + "serde", + "serde_cbor", + "serde_json", + "ssi-bbs", + "ssi-caips", + "ssi-claims-core", + "ssi-contexts", + "ssi-core", + "ssi-crypto", + "ssi-data-integrity-core", + "ssi-di-sd-primitives", + "ssi-eip712", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-multicodec", + "ssi-rdf", + "ssi-security", + "ssi-verification-methods", + "static-iref", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-di-sd-primitives" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d82a3693551b8363c1ce74ce4e35ce51244495da0a1e80fd94f881c871e5674" +dependencies = [ + "base64 0.12.3", + "digest 0.10.7", + "getrandom", + "hex", + "hmac", + "iref", + "linked-data", + "rdf-types", + "serde", + "sha2 0.10.8", + "ssi-json-ld", + "ssi-rdf", + "thiserror", + "uuid", +] + +[[package]] +name = "ssi-dids" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60c37820fbb7df6b202817d43781c8c9488ed88a127e0b4ffa73ce70b507f407" +dependencies = [ + "did-ethr", + "did-ion", + "did-jwk", + "did-method-key", + "did-pkh", + "did-tz", + "did-web", + "ssi-dids-core", + "ssi-jwk", + "thiserror", +] + +[[package]] +name = "ssi-dids-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00e795981717258410174f3d31157846d13271e2251766b59c802b9553bd2f" +dependencies = [ + "async-trait", + "iref", + "percent-encoding", + "pin-project", + "reqwest 0.11.27", + "serde", + "serde_json", + "serde_urlencoded", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-verification-methods-core", + "static-iref", + "thiserror", +] + +[[package]] +name = "ssi-eip712" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54107b8d19db3e8c7e65d04910a118172d636ecd1819f4691fa6c0b2428d62bc" +dependencies = [ + "hex", + "indexmap 2.5.0", + "iref", + "json-syntax", + "keccak-hash", + "linked-data", + "rdf-types", + "serde", + "serde_jcs", + "serde_json", + "thiserror", +] + +[[package]] +name = "ssi-json-ld" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15dd9afa49d9c980304622d0d548f7b7f6e42bf5212504caff55e5e0af218c45" +dependencies = [ + "async-std", + "combination", + "futures", + "iref", + "json-ld", + "json-syntax", + "lazy_static", + "linked-data", + "locspan", + "rdf-types", + "serde", + "ssi-contexts", + "ssi-crypto", + "ssi-rdf", + "static-iref", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-jwk" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9548df4ddf50fe2ce3ad91f8505d1babf0e18cfc28b7b3d0e5d096990764d195" +dependencies = [ + "base64 0.12.3", + "blake2b_simd 0.5.11", + "bs58", + "ed25519-dalek", + "getrandom", + "json-syntax", + "k256", + "lazy_static", + "linked-data", + "multibase 0.9.1", + "num-bigint", + "num-derive", + "num-traits", + "p256", + "rand", + "rsa", + "serde", + "serde_jcs", + "serde_json", + "simple_asn1", + "ssi-claims-core", + "ssi-crypto", + "ssi-multicodec", + "thiserror", + "zeroize", +] + +[[package]] +name = "ssi-jws" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcacab32207ac1ba86912e735cc4c46d7f66a0cbfbe82a10adcfa248927c44c7" +dependencies = [ + "base64 0.12.3", + "blake2", + "clear_on_drop", + "ed25519-dalek", + "hex", + "iref", + "k256", + "linked-data", + "p256", + "rand", + "rsa", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-jwk", + "thiserror", +] + +[[package]] +name = "ssi-jwt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa87eaf3b943eed8185ebb94b0b5a462c8ebc6b27fff0c4e0c474d51ee369a39" +dependencies = [ + "async-trait", + "chrono", + "hashbrown 0.14.5", + "iref", + "json-syntax", + "ordered-float 4.2.2", + "serde", + "serde_json", + "serde_with 2.3.3", + "slab", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-jwk", + "ssi-jws", + "thiserror", +] + +[[package]] +name = "ssi-multicodec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fec8cbdadfc90aec4839fd79f03aa887364f5f210c3aecbf9c6f7d76ab79055" +dependencies = [ + "csv", + "ed25519-dalek", + "k256", + "p256", + "p384", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "ssi-rdf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07a869c976c5c84c1fb605a95a153da92a3a0472f4233ea776282f27ef15b11" +dependencies = [ + "combination", + "indexmap 2.5.0", + "iref", + "linked-data", + "rdf-types", + "serde", + "ssi-crypto", +] + +[[package]] +name = "ssi-sd-jwt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78be0bc0774fc8409e8820b94e0eb4bef3c168f7791ac4e28d379cb135708a03" +dependencies = [ + "base64 0.12.3", + "rand", + "serde", + "serde_json", + "sha2 0.10.8", + "ssi-jwk", + "ssi-jws", + "ssi-jwt", + "thiserror", +] + +[[package]] +name = "ssi-security" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f870a28eabccd4ae0e807122859bb83e36b87dd096ba01215abce524bddfc247" +dependencies = [ + "iref", + "linked-data", + "multibase 0.9.1", + "serde", + "static-iref", + "xsd-types", +] + +[[package]] +name = "ssi-ssh" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f475a6cd9c30cbe61c556b03fb26e9e82075c3ba28e27a2da360ac778a00b4a" +dependencies = [ + "sshkeys", + "ssi-jwk", + "thiserror", +] + +[[package]] +name = "ssi-status" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d72aecae429a1b88a22cd1caf90c61f24eafc97432e67a24eb8d2e09fde0eb0" +dependencies = [ + "base64 0.12.3", + "flate2", + "iref", + "log", + "multibase 0.9.1", + "parking_lot", + "rdf-types", + "reqwest 0.12.7", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-data-integrity", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "ssi-jwt", + "ssi-vc", + "ssi-verification-methods", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-ucan" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc87074ce94abeadbe67b7b5863b398f065b19b65b7cb875d70cbe853b91cc5d" +dependencies = [ + "base64 0.12.3", + "bs58", + "chrono", + "hex", + "iref", + "libipld", + "multibase 0.9.1", + "serde", + "serde_json", + "serde_with 1.14.0", + "ssi-caips", + "ssi-core", + "ssi-dids-core", + "ssi-jwk", + "ssi-jws", + "ssi-jwt", + "ssi-verification-methods", + "thiserror", +] + +[[package]] +name = "ssi-vc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b934a74bd79137a4084aff2c0eafc5f0f3468ea4058032db44d3686cceaa0bd" +dependencies = [ + "base64 0.12.3", + "bitvec 0.20.4", + "chrono", + "educe 0.4.23", + "flate2", + "iref", + "json-syntax", + "linked-data", + "rdf-types", + "reqwest 0.11.27", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-core", + "ssi-data-integrity", + "ssi-dids-core", + "ssi-json-ld", + "ssi-jwt", + "ssi-rdf", + "ssi-verification-methods", + "static-iref", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-vc-jose-cose" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7fdc17c83808bf91c47647a40b9e91af052ee930da77f3336536f3250f35e1" +dependencies = [ + "serde", + "serde_json", + "ssi-claims-core", + "ssi-json-ld", + "ssi-jws", + "ssi-vc", + "thiserror", + "xsd-types", +] + +[[package]] +name = "ssi-verification-methods" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc89660a8bcf21a153792b846c1431e13544e4892660a6863f092bb5f2556d79" +dependencies = [ + "async-trait", + "derivative", + "ed25519-dalek", + "educe 0.4.23", + "futures", + "hex", + "iref", + "json-syntax", + "k256", + "linked-data", + "multibase 0.9.1", + "p256", + "pin-project", + "rand_core", + "rdf-types", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3", + "ssi-caips", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-eip712", + "ssi-jwk", + "ssi-jws", + "ssi-multicodec", + "ssi-security", + "ssi-verification-methods-core", + "static-iref", + "thiserror", +] + +[[package]] +name = "ssi-verification-methods-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a96a8b0881b0198076ceb078b48e02bf83fe42f1a0e1c500c0546218cf964d6" +dependencies = [ + "bs58", + "educe 0.4.23", + "hex", + "iref", + "linked-data", + "multibase 0.9.1", + "rdf-types", + "serde", + "serde_json", + "ssi-claims-core", + "ssi-core", + "ssi-crypto", + "ssi-json-ld", + "ssi-jwk", + "ssi-jws", + "static-iref", + "thiserror", +] + +[[package]] +name = "ssi-zcap-ld" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f39422229a20f7f26cad86fdc277cb165786695cbf1b389398a0f777a39333" +dependencies = [ + "async-trait", + "iref", + "json-syntax", + "rdf-types", + "serde", + "serde_json", + "ssi-claims", + "ssi-core", + "ssi-dids-core", + "ssi-eip712", + "ssi-json-ld", + "ssi-jwk", + "ssi-rdf", + "ssi-verification-methods", + "static-iref", + "thiserror", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static-iref" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc4068497ae43896d41174586dcdc2153a1af2c82856fb308bfaaddc28e5549" +dependencies = [ + "iref", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "static-regular-grammar" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957" +dependencies = [ + "abnf", + "btree-range-map", + "ciborium", + "hex_fmt", + "indoc", + "proc-macro-error", + "proc-macro2", + "quote", + "serde", + "sha2 0.10.8", + "syn 2.0.77", + "thiserror", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys 0.6.0", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[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 = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[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.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.12", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.21.0", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.23.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.5.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[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.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treediff" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" +dependencies = [ + "serde_json", +] + +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-decode" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca61eb27fa339aa08826a29f03e87b99b4d8f0fc2255306fd266bb1b6a9de498" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[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.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[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.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[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", + "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_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[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_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "zeroize", +] + +[[package]] +name = "xsd-types" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de477815bf53cc3ed3156443b7a83ff1e85b81d67df415b791f9cf830f5f4eda" +dependencies = [ + "chrono", + "iref", + "lazy_static", + "num-bigint", + "num-rational", + "num-traits", + "once_cell", + "ordered-float 3.9.2", + "pretty_dtoa", + "serde", + "static-iref", + "static-regular-grammar", + "thiserror", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +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 2.0.77", +] + +[[package]] +name = "zkryptium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c734c171ed591a19dc1127351eb1b4d91864d3e53b2b6e9992bffcb7febf364a" +dependencies = [ + "bls12_381_plus", + "cargo-license", + "digest 0.10.7", + "dotenv", + "elliptic-curve", + "env_logger", + "ff 0.13.0", + "group 0.10.0", + "hex", + "hkdf", + "log", + "rand", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3", + "thiserror", + "zeroize", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..68662ae --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,63 @@ +[workspace] +members = [ + "affinidi-did-resolver-methods/did-peer", + "affinidi-did-resolver-cache-sdk", + "affinidi-did-resolver-cache-server", + "affinidi-did-resolver-methods", +] +resolver = "2" + +[workspace.package] +version = "0.1.5" +edition = "2021" +authors = ["Glenn Gore "] +description = "Affinidi DID Resolver" +readme = "README.md" +homepage = "https://affinidi.com/" +keywords = ["did", "ssi"] +publish = true +license = "Apache-2.0" +repository = "https://github.com/affinidi/affinidi-did-resolver" + +[workspace.dependencies] + +# Common Dependencies +moka = { version = "0.12.8", features = ["future"] } +rand = "0.8" +regex = "1" +serde = { version = "1.0", features = ["derive", "rc"] } +serde_json = "1.0" +blake2 = "0.10" +ssi = "0.8" +thiserror = "1.0" +tokio = { version = "1.40", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +# Cache Server +affinidi-did-resolver-cache-sdk = { version = "0.1", path = "./affinidi-did-resolver-cache-sdk" } +axum = { version = "0.7", features = ["ws"] } +axum-extra = { version = "0.9.3", features = ["typed-header"] } +axum-server = { version = "0.7", features = ["tls-rustls"] } +did-peer = { version = "0.1", path = "./affinidi-did-resolver-methods/did-peer" } +chrono = "0.4" +http = "1" +toml = "0.8" +tower-http = { version = "0.5", features = ["cors", "trace"] } + +# Cache Client (SDK) +futures-util = "0.3" +tokio-tungstenite = "0.23" +rayon = "1.10" +num-format = "0.4.4" +clap = { version = "4.5", features = ["derive"] } +number_prefix = "0.4" + +# DID methods +askar-crypto = { version = "0.3.1", features = ["alloc"] } +base64 = "0.22" +iref = { version = "3.2.2", features = ["serde"] } +multibase = "0.9.1" +serde-wasm-bindgen = "0.6.5" +wasm-bindgen = "0.2.84" +wasm-bindgen-futures = "0.4.42" diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..d51557b --- /dev/null +++ b/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2024] [Affinidi Pte. Ltd.] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..6709a07 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1 @@ +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the this notice when they are further distributing this code. \ No newline at end of file diff --git a/README.md b/README.md index a9fd2d9..aa6ade4 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ -# affinidi-did-resolver +# Affinidi Trust Network - Affinidi DID Resolver + +Library of useful Decentralized Identifier (DID) libraries. + +## Crate Structure + +- affinidi-did-resolver-cache-sdk + + - Developer friendly crate to instantiate either a local or network DID resolver with caching. + - List of supported DID Methods is listed in the SDK README. + +- affinidi-did-resolver-cache-server + + Remote server that resolves and caches DID Documents at scale. + +- affinidi-did-resolver-methods + + Individual custom DID Method implementations reside here. + +## Getting Started? + +### I want to start resolving DID's + +1. Read the affinidi-did-resolver-cache-sdk documentation, and get started with the example code. + +### I want to run a production network server for scale and offloading DID method resolving? + +1. Read the affinidi-did-resolver-cache-server documentation, fire it up as a service where ever you like. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a87de25 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1 @@ +To report a security issue, please email with a description of the issue, the steps you took to create the issue, affected versions, and, if known, mitigations for the issue. Our vulnerability management team will respond within 5 working days of your email. If the issue is confirmed as a vulnerability, we will open a Security Advisory. This project follows a 120 day disclosure timeline. \ No newline at end of file diff --git a/affinidi-did-resolver-cache-sdk/Cargo.toml b/affinidi-did-resolver-cache-sdk/Cargo.toml new file mode 100644 index 0000000..30be43f --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "affinidi-did-resolver-cache-sdk" +version.workspace = true +description = "Affinidi DID Resolver SDK" +edition.workspace = true +authors.workspace = true +readme.workspace = true +homepage.workspace = true +keywords.workspace = true +publish.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +blake2.workspace = true +did-peer.workspace = true +futures-util.workspace = true +moka.workspace = true +rand.workspace = true +serde.workspace = true +serde_json.workspace = true +ssi.workspace = true +thiserror.workspace = true +tokio.workspace = true +tokio-tungstenite.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true + +[dev-dependencies] +clap.workspace = true +num-format.workspace = true +number_prefix.workspace = true +rayon.workspace = true diff --git a/affinidi-did-resolver-cache-sdk/README.md b/affinidi-did-resolver-cache-sdk/README.md new file mode 100644 index 0000000..bf900a6 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/README.md @@ -0,0 +1,107 @@ +# Affinidi Trust Network - DID Universal Resolver Cache SDK + +Provides local caching for DID resolution and caching of DID Documents + +You can use this SDK in either local (resolving occurs locally) or in network (requests are forwarded to a remote server) mode. + +## Supported DID Methods + + 1. did:key + 2. did:ethr + 3. did:jwk + 4. did.pkh + 5. did.peer + 6. did.web + +## Prerequisites + +Rust version 1.79 + +NOTE: For network mode, you will need access to a running a DID Universal Resolver Cache! + +## Local Mode + +A local cache is operated and all DID resolving is handled from the client itself. + +When handling DID methods that require network access (e.g did:web), then the client will call those network services. + +### Example Local mode with defaults + +```rust + use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, errors::DIDCacheError, DIDCacheClient}; + + // Create a new local client configuration, use default values + let local_config = ClientConfigBuilder::default().build(); + let local_resolver = DIDCacheClient::new(local_config).await?; + let doc = local_resolver.resolve("did:key:...").await?; + + match local_resolver.resolve(peer_did).await { + Ok(request) => { + println!( + "Resolved DID ({}) did_hash({}) Document:\n{:#?}\n", + request.did, request.did_hash, request.doc + ); + } + Err(e) => { + println!("Error: {:?}", e); + } + } +``` + +## Network Mode + +NOTE: When in network mode, the SDK will still cache locally to save on remote calls! + +All DID resolving is handled remotely, just the DID Document is returned and cached locally. + +### Example Network mode with optional settings + +```rust + use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, errors::DIDCacheError, DIDCacheClient}; + + // create a network client configuration, set the service address. + let network_config = ClientConfigBuilder::default() + .with_network_mode("ws://127.0.0.1:8080/did/v1/ws") + .with_cache_ttl(60) // Change the cache TTL to 60 seconds + .with_network_timeout(20_000) // Change the network timeout to 20 seconds + .build(); + let network_resolver = DIDCacheClient::new(network_config).await?; + + match local_resolver.resolve("did:key:...").await { + Ok((request) => { + println!( + "Resolved DID ({}) did_hash({}) Document:\n{:#?}\n", + request.did, request.did_hash, request.doc + ); + } + Err(e) => { + println!("Error: {:?}", e); + } + } +``` + +## Running benchmark suite for testing + +A reference benchmark example is included that can be used to measure performance. To run this use the following: + +`cargo run --example benchmark -- -g 1000 -r 10000 -n ws://127.0.0.1:8080/did/v1/ws` + +Run the above from the $affinidi-did-resolver/affinidi-did-resolver-cache-sdk directory + +``` +Affinidi DID Cache SDK + +Usage: benchmark [OPTIONS] --generate-count --resolve-count + +Options: +-n, --network-address + network address if running in network mode (ws://127.0.0.1:8080/did/v1/ws) +-g, --generate-count + Number of keys to generate +-r, --resolve-count + Number of DIDs to resolve +-h, --help + Print help +-V, --version + Print version +``` diff --git a/affinidi-did-resolver-cache-sdk/examples/benchmark.rs b/affinidi-did-resolver-cache-sdk/examples/benchmark.rs new file mode 100644 index 0000000..8395ab0 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/examples/benchmark.rs @@ -0,0 +1,249 @@ +//! Runs a series of performance benchmarks against the DID cache. +//! Benchmark references: (Apple M1 Max) +//! - 1 million did:key's generated in ~4.2 seconds, consumes 2.3MiB of memory +use affinidi_did_resolver_cache_sdk::{ + config::ClientConfigBuilder, errors::DIDCacheError, DIDCacheClient, +}; +use clap::Parser; +use futures_util::future::join_all; +use num_format::{Locale, ToFormattedString}; +use number_prefix::NumberPrefix; +use rand::Rng; +use rayon::prelude::*; +use ssi::prelude::DIDResolver; +use ssi::{ + dids::{DIDKey, DID}, + JWK, +}; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::Mutex; +use tracing_subscriber::filter; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// network address if running in network mode (ws://127.0.0.1:8080/did/v1/ws) + #[arg(short, long)] + network_address: Option, + /// Number of keys to generate + #[arg(short, long)] + generate_count: u32, + /// Number of DIDs to resolve + #[arg(short, long)] + resolve_count: u32, +} + +#[tokio::main] +async fn main() -> Result<(), DIDCacheError> { + // ************************************************************** + // *** Initial setup + // ************************************************************** + let args = Args::parse(); + + // construct a subscriber that prints formatted traces to stdout + let subscriber = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .with_env_filter(filter::EnvFilter::from_default_env()) + .finish(); + // use that subscriber to process traces emitted after this point + tracing::subscriber::set_global_default(subscriber).expect("Logging failed, exiting..."); + + let mut cache_config = ClientConfigBuilder::default(); + if let Some(address) = &args.network_address { + println!("Running in network mode with address: {}", address); + cache_config = cache_config.with_network_mode(address); + } else { + println!("Running in local mode."); + } + + let cache = DIDCacheClient::new(cache_config.build()).await?; + println!("Cache initialized..."); + + // ************************************************************** + // *** Generate DIDs + // ************************************************************** + + settle_down(5).await; + + println!( + "Generating ({}) keys...", + args.generate_count.to_formatted_string(&Locale::en) + ); + let dids = generate_dids(args.generate_count).await; + let dids_arc = Arc::new(dids.clone()); + + // ************************************************************** + // *** Resolve DIDs - NO CACHE + // ************************************************************** + + settle_down(5).await; + + resolve_dids_no_cache(dids_arc.clone(), args.resolve_count).await?; + + // ************************************************************** + // *** Resolve DIDs - CACHE + // ************************************************************** + + settle_down(5).await; + + resolve_dids(&cache, dids_arc.clone(), args.resolve_count).await?; + + // ************************************************************** + // *** Resolve DIDs - CACHE - 2nd run (now has local cache) + // ************************************************************** + + settle_down(5).await; + + //resolve_dids(&cache, dids_arc.clone(), args.resolve_count).await?; + + Ok(()) +} + +/// Goes to sleep to let system settle down before/after a benchmark test +async fn settle_down(secs: u64) { + println!(); + println!("Sleeping for 5 seconds to let the system settle down..."); + tokio::time::sleep(Duration::from_secs(secs)).await; +} + +/// Pretty print a float number (1,000.24) +/// Adds comma's and rounds to 2 decimal places +fn pretty_print_float(f: f64) -> String { + let a = f as u64; + let b = (f.fract() * 100.0) as u32; + format!("{}.{}", a.to_formatted_string(&Locale::en), b) +} + +/// Pretty print a binary size (1.2 KiB) +fn pretty_print_binary_size(n: f64) -> String { + match NumberPrefix::binary(n) { + NumberPrefix::Standalone(bytes) => { + format!("{} bytes", bytes) + } + NumberPrefix::Prefixed(prefix, n) => { + format!("{:.1} {}B", n, prefix) + } + } +} + +/// Generates a set of keys for testing purposes, can do in parallel +/// count: number of keys to generate +async fn generate_dids(count: u32) -> Vec { + let _start = std::time::Instant::now(); + let dids: Vec = (0..count) + .into_par_iter() + .map(|x| { + let key = if x % 2 == 0 { + JWK::generate_ed25519().unwrap() + } else { + JWK::generate_secp256k1() + }; + + DIDKey::generate(&key).unwrap().to_string() + }) + .collect(); + + let elapsed = _start.elapsed(); + let per_second = count as f64 / elapsed.as_secs_f64(); + println!( + "Generated 100,000 did:key's in {} seconds :: {} keys/second :: Memory used: {}", + pretty_print_float(_start.elapsed().as_secs_f64()), + pretty_print_float(per_second), + pretty_print_binary_size(size_of_val(&*dids) as f64) + ); + dids +} + +async fn resolve_dids_no_cache(dids: Arc>, count: u32) -> Result<(), DIDCacheError> { + println!("Resolving {} dids, NO-CACHE!!!", count); + let dids_len = dids.len(); + let mut handles = Vec::new(); + + let _start = std::time::Instant::now(); + let mut rng = rand::thread_rng(); + for _ in 0..count { + let r: usize = rng.gen::() % dids_len; + let _dids = dids.clone(); + + //let _cache = cache.clone(); + handles.push(tokio::spawn(async move { + let method = DIDKey; + + let _ = match method + .resolve(DID::new::(_dids.get(r).unwrap()).unwrap()) + .await + { + Ok(res) => Some(res.document.into_document()), + Err(e) => { + eprintln!("Error: {:?}", e); + None + } + }; + })); + } + join_all(handles).await; + + let elapsed = _start.elapsed(); + let per_second = count as f64 / elapsed.as_secs_f64(); + println!( + "NO-CACHE: Resolved {} did:key's in {} seconds :: {} keys/second", + count, + pretty_print_float(_start.elapsed().as_secs_f64()), + pretty_print_float(per_second) + ); + + Ok(()) +} + +async fn resolve_dids( + cache: &DIDCacheClient, + dids: Arc>, + count: u32, +) -> Result<(), DIDCacheError> { + let cache_hit: Arc> = Arc::new(Mutex::new(0)); + + let dids_len = dids.len(); + let mut handles = Vec::new(); + + let _start = std::time::Instant::now(); + let mut rng = rand::thread_rng(); + + for _ in 0..count { + let r: usize = rng.gen::() % dids_len; + let _dids = dids.clone(); + + let _cache = cache.clone(); + let _cache_hit = cache_hit.clone(); + + handles.push(tokio::spawn(async move { + match _cache.resolve(_dids.get(r).unwrap()).await { + Ok(res) => { + println!("Resolved DID: ({}) cache_hit?({})", res.did, res.cache_hit); + if res.cache_hit { + //let mut lock = _cache_hit.lock().await; + //*lock += 1; + } + } + Err(e) => { + eprintln!("Error: {:?}", e); + } + } + })); + } + join_all(handles).await; + + let elapsed = _start.elapsed(); + let per_second = count as f64 / elapsed.as_secs_f64(); + let _cache_hit = *cache_hit.lock().await; + println!( + "Resolved {} did:key's in {} seconds :: {} keys/second :: cache_hits: {} {}%", + count, + pretty_print_float(_start.elapsed().as_secs_f64()), + pretty_print_float(per_second), + _cache_hit, + pretty_print_float((_cache_hit as f64 / count as f64) * 100.0) + ); + + Ok(()) +} diff --git a/affinidi-did-resolver-cache-sdk/examples/resolve.rs b/affinidi-did-resolver-cache-sdk/examples/resolve.rs new file mode 100644 index 0000000..1930b7f --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/examples/resolve.rs @@ -0,0 +1,131 @@ +use affinidi_did_resolver_cache_sdk::{ + config::ClientConfigBuilder, errors::DIDCacheError, DIDCacheClient, +}; +use clap::Parser; +use tracing_subscriber::filter; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// network address if running in network mode (ws://127.0.0.1:8080/did/v1/ws) + #[arg(short, long)] + network_address: Option, +} + +#[tokio::main] +async fn main() -> Result<(), DIDCacheError> { + // ************************************************************** + // *** Initial setup + // ************************************************************** + let args = Args::parse(); + + // construct a subscriber that prints formatted traces to stdout + let subscriber = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .with_env_filter(filter::EnvFilter::from_default_env()) + .finish(); + // use that subscriber to process traces emitted after this point + tracing::subscriber::set_global_default(subscriber).expect("Logging failed, exiting..."); + + // test did + let peer_did = "did:peer:2.Vz6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv.EzQ3shQLqRUza6AMJFbPuMdvFRFWm1wKviQRnQSC1fScovJN4s.SeyJ0IjoiRElEQ29tbU1lc3NhZ2luZyIsInMiOnsidXJpIjoiaHR0cHM6Ly8xMjcuMC4wLjE6NzAzNyIsImEiOlsiZGlkY29tbS92MiJdLCJyIjpbXX19"; + + println!(); + println!(" ****************************** "); + println!(" * Local Resolver Example * "); + println!(" ****************************** "); + println!(); + + // Create a new local client configuration, use default values + let local_config = ClientConfigBuilder::default().build(); + let local_resolver = DIDCacheClient::new(local_config).await?; + match local_resolver.resolve(peer_did).await { + Ok(response) => { + println!( + "Resolved DID ({}) did_hash({}) Document:\n{:#?}\n", + response.did, response.did_hash, response.doc + ); + } + Err(e) => { + println!("Error: {:?}", e); + } + } + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" ****************************** "); + println!(); + // create a network client configuration, set the service address. + let mut network_config = ClientConfigBuilder::default() + .with_cache_ttl(60) // Change the cache TTL to 60 seconds + .with_network_timeout(20_000); // Change the network timeout to 20 seconds + if let Some(address) = &args.network_address { + println!("Running in network mode with address: {}", address); + network_config = network_config.with_network_mode(address); + } else { + println!("Running in local mode."); + } + + let network_resolver = DIDCacheClient::new(network_config.build()).await?; + let response = network_resolver.resolve(peer_did).await?; + + println!("Resolved DID Document: {:#?}", response.doc); + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" * did:key method * "); + println!(" ****************************** "); + println!(); + let response = network_resolver + .resolve("did:key:z6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv") + .await?; + println!("Resolved DID Document: {:#?}", response.doc); + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" * did:web method * "); + println!(" ****************************** "); + println!(); + let response = network_resolver.resolve("did:web:affinidi.com").await?; + println!("Resolved DID Document: {:#?}", response.doc); + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" * did:ethr method * "); + println!(" ****************************** "); + println!(); + let response = network_resolver + .resolve("did:ethr:0x1:0xb9c5714089478a327f09197987f16f9e5d936e8a") + .await?; + println!("Resolved DID Document: {:#?}", response.doc); + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" * did:jwk method * "); + println!(" ****************************** "); + println!(); + let response = network_resolver + .resolve("did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9") + .await?; + println!("Resolved DID Document: {:#?}", response.doc); + + println!(); + println!(" ****************************** "); + println!(" * Network Resolver Example * "); + println!(" * did:pkh method * "); + println!(" ****************************** "); + println!(); + let response = network_resolver + .resolve( + "did:pkh:solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ:CKg5d12Jhpej1JqtmxLJgaFqqeYjxgPqToJ4LBdvG9Ev", + ) + .await?; + println!("Resolved DID Document: {:#?}", response.doc); + + Ok(()) +} diff --git a/affinidi-did-resolver-cache-sdk/src/config.rs b/affinidi-did-resolver-cache-sdk/src/config.rs new file mode 100644 index 0000000..daa436f --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/config.rs @@ -0,0 +1,112 @@ +//! Handles the initial configuration for the DID Cache Client. +//! +//! Call the [ClientConfigBuilder] to create a new configuration. +//! +//! Example: Running in local mode with defaults: +//! ```rust +//! use affinidi_did_resolver_cache_sdk::config::ClientConfigBuilder; +//! let config = ClientConfigBuilder::default().build(); +//! ``` +//! +//! Example: Running in network mode with custom settings: +//! ```rust +//! use affinidi_did_resolver_cache_sdk::config::ClientConfigBuilder; +//! let config = ClientConfigBuilder::default() +//! .with_network_mode("ws://127.0.0.1:8080/did/v1/ws") +//! .with_cache_capacity(500) +//! .with_cache_ttl(60) +//! .with_network_timeout(10000) +//! .with_network_cache_limit_count(200) +//! .build(); +//! ``` +//! + +use std::time::Duration; + +/// Private Configuration for the client. +/// +/// Use the [ClientConfigBuilder] to create a new configuration. +#[derive(Clone, Debug)] +pub struct ClientConfig { + pub(crate) service_address: Option, + pub(crate) cache_capacity: u32, + pub(crate) cache_ttl: u32, + pub(crate) network_timeout: Duration, + pub(crate) network_cache_limit_count: u32, +} + +/// Config Builder to construct options required for the client. +/// You must at least set the service address. +/// +/// - service_address: REQUIRED: The address of the service to connect to. +/// - cache_capacity: The maximum number of items to store in the local cache (default: 100). +/// - cache_ttl: The time-to-live in seconds for each item in the local cache (default: 300 (5 Minutes)). +/// - network_timeout: The timeout for network requests in milliseconds (default: 5000 (5 seconds)). +/// - network_cache_limit_count: The maximum number of items to store in the network cache (default: 100). +pub struct ClientConfigBuilder { + service_address: Option, + cache_capacity: u32, + cache_ttl: u32, + network_timeout: u32, + network_cache_limit_count: u32, +} + +impl Default for ClientConfigBuilder { + fn default() -> Self { + Self { + service_address: None, + cache_capacity: 100, + cache_ttl: 300, + network_timeout: 5000, + network_cache_limit_count: 100, + } + } +} + +impl ClientConfigBuilder { + /// Enables network mode and sets the service address. + /// Example: `ws://127.0.0.1:8080/did/v1/ws` + pub fn with_network_mode(mut self, service_address: &str) -> Self { + self.service_address = Some(service_address.into()); + self + } + + /// Set the cache capacity (approx) + /// Default: 100 items + pub fn with_cache_capacity(mut self, cache_capacity: u32) -> Self { + self.cache_capacity = cache_capacity; + self + } + + /// Set the time-to-live in seconds for each item in the local cache. + /// Default: 300 (5 Minutes) + pub fn with_cache_ttl(mut self, cache_ttl: u32) -> Self { + self.cache_ttl = cache_ttl; + self + } + + /// Set the timeout for network requests in milliseconds. + /// Default: 5000 (5 seconds) + pub fn with_network_timeout(mut self, network_timeout: u32) -> Self { + self.network_timeout = network_timeout; + self + } + + /// Set the network cache limit count + /// Default: 100 items + pub fn with_network_cache_limit_count(mut self, limit_count: u32) -> Self { + self.network_cache_limit_count = limit_count; + self + } + + /// Build the [ClientConfig]. + pub fn build(self) -> ClientConfig { + ClientConfig { + service_address: self.service_address, + cache_capacity: self.cache_capacity, + cache_ttl: self.cache_ttl, + network_timeout: Duration::from_millis(self.network_timeout.into()), + network_cache_limit_count: self.network_cache_limit_count, + } + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/document.rs b/affinidi-did-resolver-cache-sdk/src/document.rs new file mode 100644 index 0000000..87b1e7e --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/document.rs @@ -0,0 +1,153 @@ +//! Extends the SSI Crate Document with new methods and functions + +use ssi::{ + dids::{ + document::{DIDVerificationMethod, ResourceRef}, + Document, DIDURL, + }, + verification_methods::ProofPurposes, +}; +use tracing::warn; + +pub trait DocumentExt { + /// Does this DID contain authentication verification_method with the given id? + fn contains_authentication(&self, id: &str) -> bool; + + /// Does this DID contain a key agreement with the given id? + fn contains_key_agreement(&self, id: &str) -> bool; + + /// find_key_agreement or return all + /// Returns fully defined Vec of key_agreement id's + fn find_key_agreement(&self, id: Option<&str>) -> Vec; + + /// Returns a DID Verification Method if found by ID + fn get_verification_method(&self, id: &str) -> Option<&DIDVerificationMethod>; +} + +impl DocumentExt for Document { + fn contains_authentication(&self, id: &str) -> bool { + let id = if let Ok(id) = DIDURL::new(id.as_bytes()) { + id + } else { + return false; + }; + + self.verification_relationships.contains( + &self.id, + id, + ProofPurposes { + assertion_method: false, + authentication: true, + key_agreement: false, + capability_delegation: false, + capability_invocation: false, + }, + ) + } + + fn contains_key_agreement(&self, id: &str) -> bool { + let id = if let Ok(id) = DIDURL::new(id.as_bytes()) { + id + } else { + return false; + }; + + self.verification_relationships.contains( + &self.id, + id, + ProofPurposes { + assertion_method: false, + authentication: false, + key_agreement: true, + capability_delegation: false, + capability_invocation: false, + }, + ) + } + + fn find_key_agreement(&self, id: Option<&str>) -> Vec { + if let Some(id) = id { + // Does this id exist in key_agreements? + if self.contains_key_agreement(id) { + vec![id.to_string()] + } else { + vec![] + } + } else { + let did = self.id.as_did(); + self.verification_relationships + .key_agreement + .iter() + .map(|ka| ka.id().resolve(did).to_string()) + .collect() + } + } + + fn get_verification_method(&self, id: &str) -> Option<&DIDVerificationMethod> { + let id_url = match DIDURL::new(id.as_bytes()) { + Ok(id) => id, + Err(_) => { + warn!("Invalid DID URL: {}", id); + return None; + } + }; + + if let Some(resource) = self.find_resource(id_url) { + match resource { + ResourceRef::VerificationMethod(method) => Some(method), + _ => { + warn!("Resource is not a verification method: {}", id); + None + } + } + } else { + warn!("Resource not found: {}", id); + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{config, DIDCacheClient}; + + use super::*; + + const TEST_DID: &str = "did:peer:2.Vz6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv.EzQ3shQLqRUza6AMJFbPuMdvFRFWm1wKviQRnQSC1fScovJN4s.SeyJ0IjoiRElEQ29tbU1lc3NhZ2luZyIsInMiOnsidXJpIjoiaHR0cHM6Ly8xMjcuMC4wLjE6NzAzNyIsImEiOlsiZGlkY29tbS92MiJdLCJyIjpbXX19"; + + async fn basic_local_client() -> DIDCacheClient { + let config = config::ClientConfigBuilder::default().build(); + DIDCacheClient::new(config).await.unwrap() + } + + #[tokio::test] + async fn key_agreement_id_exists() { + let client = basic_local_client().await; + + // Resolve a DID which automatically adds it to the cache + let response = client.resolve(TEST_DID).await.unwrap(); + assert!(response + .doc + .contains_key_agreement(&[TEST_DID, "#key-2"].concat())); + } + + #[tokio::test] + async fn key_agreement_id_missing() { + let client = basic_local_client().await; + + // Resolve a DID which automatically adds it to the cache + let response = client.resolve(TEST_DID).await.unwrap(); + assert!(!response + .doc + .contains_key_agreement(&[TEST_DID, "#key-3"].concat())); + } + + #[tokio::test] + async fn invalid_key_agreement() { + let client = basic_local_client().await; + + // Resolve a DID which automatically adds it to the cache + let response = client.resolve(TEST_DID).await.unwrap(); + assert!(!response.doc.contains_key_agreement("BAD_DID:TEST#FAIL")); + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/errors.rs b/affinidi-did-resolver-cache-sdk/src/errors.rs new file mode 100644 index 0000000..236d5a2 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/errors.rs @@ -0,0 +1,24 @@ +//! Error types for the DID Cache Client SDK +use thiserror::Error; + +/// DIDCacheError is the error type for the DID Cache Client SDK. +/// +/// This error type is used for all errors that can occur in the DID Cache Client SDK. +#[derive(Error, Debug)] +pub enum DIDCacheError { + /// There was an error in resolving the DID. + #[error("DID error: {0}")] + DIDError(String), + /// Unsupported DID Method + #[error("Unsupported DID method: {0}")] + UnsupportedMethod(String), + /// An error occurred at the transport layer. + #[error("Transport error: {0}")] + TransportError(String), + /// An error occurred in the configuration. + #[error("Config error: {0}")] + ConfigError(String), + /// A network timeout occurred. + #[error("Network timeout")] + NetworkTimeout, +} diff --git a/affinidi-did-resolver-cache-sdk/src/lib.rs b/affinidi-did-resolver-cache-sdk/src/lib.rs new file mode 100644 index 0000000..c3e90b3 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/lib.rs @@ -0,0 +1,248 @@ +//! DID Universal Resolver Cache Client SDK +//! +//! Used to easily connect to the DID Universal Resolver Cache. +//! +use blake2::{Blake2s256, Digest}; +use config::ClientConfig; +use errors::DIDCacheError; +use moka::future::Cache; +use networking::{ + network::{NetworkTask, WSCommands}, + WSRequest, +}; +use ssi::dids::Document; +use std::{fmt, sync::Arc, time::Duration}; +use tokio::sync::{mpsc, Mutex}; +use tracing::debug; + +pub mod config; +pub mod document; +pub mod errors; +pub mod networking; +mod resolver; + +/// DID Methods supported by the DID Universal Resolver Cache +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum DIDMethod { + ETHR, + JWK, + KEY, + PEER, + PKH, + WEB, +} + +/// Helper function to convert a DIDMethod to a string +impl fmt::Display for DIDMethod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DIDMethod::ETHR => write!(f, "ethr"), + DIDMethod::JWK => write!(f, "jwk"), + DIDMethod::KEY => write!(f, "key"), + DIDMethod::PEER => write!(f, "peer"), + DIDMethod::PKH => write!(f, "pkh"), + DIDMethod::WEB => write!(f, "web"), + } + } +} + +/// Helper function to convert a string to a DIDMethod +impl TryFrom for DIDMethod { + type Error = DIDCacheError; + + fn try_from(value: String) -> Result { + value.as_str().try_into() + } +} + +impl TryFrom<&str> for DIDMethod { + type Error = DIDCacheError; + + fn try_from(value: &str) -> Result { + match value.to_lowercase().as_str() { + "ethr" => Ok(DIDMethod::ETHR), + "jwk" => Ok(DIDMethod::JWK), + "key" => Ok(DIDMethod::KEY), + "peer" => Ok(DIDMethod::PEER), + "pkh" => Ok(DIDMethod::PKH), + "web" => Ok(DIDMethod::WEB), + _ => Err(DIDCacheError::UnsupportedMethod(value.to_string())), + } + } +} + +pub struct ResolveResponse { + pub did: String, + pub method: DIDMethod, + pub did_hash: String, + pub doc: Document, + pub cache_hit: bool, +} + +// *************************************************************************** + +/// [DIDCacheClient] is how you interact with the DID Universal Resolver Cache +/// config: Configuration for the SDK +/// cache: Local cache for resolved DIDs +/// network_task: OPTIONAL: Task to handle network requests +/// network_rx: OPTIONAL: Channel to listen for responses from the network task +#[derive(Clone)] +pub struct DIDCacheClient { + config: ClientConfig, + cache: Cache, + network_task_tx: Option>, + network_task_rx: Option>>>, +} + +impl DIDCacheClient { + /// Create a new DIDCacheClient with configuration generated from [ClientConfigBuilder](config::ClientConfigBuilder) + /// + /// Will return an error if the configuration is invalid. + /// + /// Establishes websocket connection and sets up the cache. + pub async fn new(config: ClientConfig) -> Result { + // Create the initial cache + let cache = Cache::builder() + .max_capacity(config.cache_capacity.into()) + .time_to_live(Duration::from_secs(config.cache_ttl.into())) + .build(); + + let mut client = Self { + config, + cache, + network_task_tx: None, + network_task_rx: None, + }; + + if client.config.service_address.is_some() { + // Running in network mode + + // Channel to communicate from SDK to network task + let (sdk_tx, mut task_rx) = mpsc::channel(32); + // Channel to communicate from network task to SDK + let (task_tx, sdk_rx) = mpsc::channel(32); + + client.network_task_tx = Some(sdk_tx); + client.network_task_rx = Some(Arc::new(Mutex::new(sdk_rx))); + // Start the network task + let _config = client.config.clone(); + tokio::spawn(async move { + let _ = NetworkTask::run(_config, &mut task_rx, &task_tx).await; + }); + + if let Some(arc_rx) = client.network_task_rx.as_ref() { + // Wait for the network task to be ready + let mut rx = arc_rx.lock().await; + rx.recv().await.unwrap(); + } + } + + Ok(client) + } + + /// Front end for resolving a DID + /// Will check the cache first, and if not found, will resolve the DID + /// Returns the initial DID, the hashed DID, and the resolved DID Document + /// NOTE: The DID Document id may be different to the requested DID due to the DID having been updated. + /// The original DID should be in the `also_known_as` field of the DID Document. + pub async fn resolve(&self, did: &str) -> Result { + //let did_hash = sha256::digest(did); + let mut hasher = Blake2s256::new(); + hasher.update(did); + let did_hash = format!("{:x}", hasher.finalize()); + + let parts: Vec<&str> = did.split(':').collect(); + if parts.len() < 3 { + return Err(DIDCacheError::DIDError(format!( + "did isn't to spec! did ({})", + did + ))); + } + + // Check if the DID is in the cache + if let Some(doc) = self.cache.get(&did_hash).await { + debug!("found did ({}) in cache", did); + Ok(ResolveResponse { + did: did.to_string(), + method: parts[1].try_into()?, + did_hash, + doc, + cache_hit: true, + }) + } else { + debug!("did ({}) NOT in cache hash ({})", did, did_hash); + // If the DID is not in the cache, resolve it (local or via network) + let doc = if self.config.service_address.is_none() { + debug!("resolving did ({}) locally", did); + self.local_resolve(did, &parts).await? + } else { + self.network_resolve(did, &did_hash).await? + }; + + debug!("adding did ({}) to cache ({})", did, did_hash); + self.cache.insert(did_hash.clone(), doc.clone()).await; + Ok(ResolveResponse { + did: did.to_string(), + method: parts[1].try_into()?, + did_hash, + doc, + cache_hit: false, + }) + } + } + + /// If you want to interact directly with the DID Document cache + /// This will return a clone of the cache (the clone is cheap, and the cache is shared) + /// For example, accessing cache statistics or manually inserting a DID Document + pub fn get_cache(&self) -> Cache { + self.cache.clone() + } + + /// Stops the network task if it is running and removes any resources + pub fn stop(&self) { + if let Some(tx) = self.network_task_tx.as_ref() { + let _ = tx.blocking_send(WSCommands::Exit); + } + } + + /// Removes the specified DID from the cache + /// Returns the removed DID Document if it was in the cache, or None if it was not + pub async fn remove(&self, did: &str) -> Option { + //let did_hash = sha256::digest(did); + let mut hasher = Blake2s256::new(); + hasher.update(did); + let did_hash = format!("{:x}", hasher.finalize()); + self.cache.remove(&did_hash).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const DID_KEY: &str = "did:key:z6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv"; + + async fn basic_local_client() -> DIDCacheClient { + let config = config::ClientConfigBuilder::default().build(); + DIDCacheClient::new(config).await.unwrap() + } + + #[tokio::test] + async fn remove_existing_cached_did() { + let client = basic_local_client().await; + + // Resolve a DID which automatically adds it to the cache + let response = client.resolve(DID_KEY).await.unwrap(); + let removed_doc = client.remove(DID_KEY).await; + assert_eq!(removed_doc, Some(response.doc)); + } + + #[tokio::test] + async fn remove_non_existing_cached_did() { + let client = basic_local_client().await; + + // We haven't resolved the cache, so it shouldn't be in the cache + let removed_doc = client.remove(DID_KEY).await; + assert_eq!(removed_doc, None); + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/networking/mod.rs b/affinidi-did-resolver-cache-sdk/src/networking/mod.rs new file mode 100644 index 0000000..99976a8 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/networking/mod.rs @@ -0,0 +1,136 @@ +//! Any parallel tasks that need to be run in the background. +//! +//! network task: Used to handle network requests to the DID Universal Resolver Cache. +//! network_cache: Helps with managing requests/responses that are in transit (out of order responses etc.). +//! + +use network::WSCommands; +use rand::{distributions::Alphanumeric, Rng}; +use serde::{Deserialize, Serialize}; +use ssi::dids::Document; +use tokio::{select, sync::oneshot}; +use tracing::{debug, span, warn, Instrument, Level}; + +use crate::{errors::DIDCacheError, DIDCacheClient}; +pub mod network; +mod request_queue; + +/// WSRequest is the request format to the websocket connection +/// did: DID to resolve +/// hash: SHA256 Hash of the DID +#[derive(Debug, Deserialize, Serialize)] +pub struct WSRequest { + pub did: String, + pub hash: String, +} + +/// WSResponse is the response format from the websocket connection +/// did: DID that was resolved +/// hash: SHA256 Hash of the DID +/// document: The resolved DID Document +#[derive(Debug, Deserialize, Serialize)] +pub struct WSResponse { + pub did: String, + pub hash: String, + pub document: Document, +} + +/// WSResponseError is the response format from the websocket connection if an error occurred server side. +/// did: DID associated with the error +/// hash: SHA256 Hash of the DID +/// error: Error message +#[derive(Debug, Deserialize, Serialize)] +pub struct WSResponseError { + pub did: String, + pub hash: String, + pub error: String, +} + +/// WSResponseType is the type of response received from the websocket connection +/// Response: A successful response +/// Error: An error response +#[derive(Debug, Deserialize, Serialize)] +pub enum WSResponseType { + Response(WSResponse), + Error(WSResponseError), +} + +impl DIDCacheClient { + /// Resolve a DID via the network + /// Returns the resolved DID Document, or an error + /// + /// Send the request, and wait for the response + pub(crate) async fn network_resolve( + &self, + did: &str, + did_hash: &str, + ) -> Result { + let _span = span!(Level::DEBUG, "network_resolve"); + async move { + debug!("resolving did ({}) via network hash ({})", did, did_hash); + + let network_task_tx = self.network_task_tx + .clone() + .unwrap(); + + // Set up a oneshot channel to receive the response + let (tx, rx) = oneshot::channel::(); + + // create a 8-char unique-id for this request + let unique_id: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect(); + + // 1. Send the request to the network task, which will then send via websocket to the remote server + network_task_tx + .send(WSCommands::Send(tx, unique_id.clone(), WSRequest { did: did.into(), hash: did_hash.into() })) + .await + .map_err(|e| { + DIDCacheError::TransportError(format!( + "Couldn't send request to network_task. Reason: {}", + e + )) + })?; + + // 2. Wait for the response from the network task + + // Setup the timer for the wait, doesn't do anything till `await` is called in the select! macro + let sleep = tokio::time::sleep(self.config.network_timeout); + tokio::pin!(sleep); + + select! { + _ = &mut sleep => { + warn!("Timeout reached, no message received did_hash ({})", did_hash); + network_task_tx.send(WSCommands::TimeOut(unique_id, did_hash.to_string())).await.map_err(|err| { + DIDCacheError::TransportError(format!("Could not send timeout message to ws_handler: {:?}", err)) + })?; + Err(DIDCacheError::NetworkTimeout) + } + value = rx => { + match value { + Ok(WSCommands::ResponseReceived(doc)) => { + debug!("Received response from network task ({})", did_hash); + Ok(*doc) + } + Ok(WSCommands::ErrorReceived(msg)) => { + warn!("Received error response from network task"); + Err(DIDCacheError::TransportError(msg)) + } + Ok(_) => { + debug!("Received unexpected response from network task"); + Err(DIDCacheError::TransportError("Unexpected response from network task".into())) + } + Err(e) => { + debug!("Error receiving response from network task: {:?}", e); + Err(DIDCacheError::TransportError(format!("Error receiving response from network task: {:?}", e))) + } + } + } + } + } + .instrument(_span) + .await + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/networking/network.rs b/affinidi-did-resolver-cache-sdk/src/networking/network.rs new file mode 100644 index 0000000..58a32f8 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/networking/network.rs @@ -0,0 +1,265 @@ +//! NetworkTask handles the communication with the network. +//! This runs as a separate task in the background. +//! Allows for multiplexing of multiple requests/responses to the network. +//! +//! The SDK communicates via a MPSC channel to this task. +//! The remote server communicates via a websocket connection. +//! + +use std::time::Duration; + +use crate::{config::ClientConfig, errors::DIDCacheError, WSRequest}; +use futures_util::{SinkExt, StreamExt}; +use ssi::dids::Document; +use tokio::{ + net::TcpStream, + select, + sync::{ + mpsc::{Receiver, Sender}, + oneshot, + }, + time::sleep, +}; +use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream}; +use tracing::{debug, error, span, warn, Instrument, Level}; + +use super::{request_queue::RequestList, WSResponseType}; + +/// WSCommands are the commands that can be sent between the SDK and the network task +/// Connected: Signals that the websocket is connected +/// Exit: Exits the websocket handler +/// Send: Sends the response string to the websocket (Channel, ID, WSRequest) +/// ResponseReceived: Response received from the websocket +/// ErrorReceived: Error received from the remote server +/// NotFound: Response not found in the cache +/// TimeOut: SDK request timed out, contains ID and did_hash we were looking for +#[derive(Debug)] +pub(crate) enum WSCommands { + Connected, + Exit, + Send(Responder, String, WSRequest), + ResponseReceived(Box), + ErrorReceived(String), + TimeOut(String, String), +} + +pub(crate) type Responder = oneshot::Sender; + +/// NetworkTask handles the communication with the network. +/// This runs as a separate task in the background. +/// +/// sdk_tx_channel: Sender - Channel to send commands to the network task from the SDK +/// sdk_rx_channel: Rc> - Channel to receive commands from the network task +/// task_rx_channel: Rc> - PRIVATE. Channel to receive commands from the SDK +/// task_tx_channel: Sender - PRIVATE. Channel to send commands to the SDK +/// websocket: Option>>> - PRIVATE. The websocket connection itself +pub(crate) struct NetworkTask { + config: ClientConfig, + service_address: String, + cache: RequestList, + sdk_tx: Sender, +} + +impl NetworkTask { + pub async fn run( + config: ClientConfig, + sdk_rx: &mut Receiver, + sdk_tx: &Sender, + ) -> Result<(), DIDCacheError> { + let _span = span!(Level::INFO, "network_task"); + async move { + debug!("Starting..."); + + let service_address = if let Some(service_address) = &config.service_address { + service_address.to_string() + } else { + return Err(DIDCacheError::ConfigError( + "Running in local mode, yet network service called!".to_string(), + )); + }; + + let cache = RequestList::new(&config); + + let mut network_task = NetworkTask { + config, + service_address, + cache, + sdk_tx: sdk_tx.clone(), + }; + + let mut websocket = network_task.ws_connect().await?; + + loop { + select! { + value = websocket.next() => { + if network_task.ws_recv(value).is_err() { + // Reset the connection + websocket = network_task.ws_connect().await?; + } + }, + value = sdk_rx.recv(), if !network_task.cache.is_full() => { + if let Some(cmd) = value { + match cmd { + WSCommands::Send(channel, uid, request) => { + if network_task.cache.insert(request.hash.clone(), &uid, channel) { + let _ = network_task.ws_send(&mut websocket, &request).await; + } + } + WSCommands::TimeOut(uid, did_hash) => { + let _ = network_task.cache.remove(&did_hash, Some(uid)); + } + WSCommands::Exit => { + debug!("Exiting..."); + return Ok(()); + } + _ => { + debug!("Invalid command received: {:?}", cmd); + } + } + } + } + } + } + } + .instrument(_span) + .await + } + + /// Creates the connection to the remote server via a websocket + /// If timeouts or errors occur, it will backoff and retry + /// NOTE: Increases in 5 second increments up to 60 seconds + async fn ws_connect( + &self, + ) -> Result>, DIDCacheError> { + async fn _handle_backoff(backoff: Duration) -> Duration { + let b = if backoff.as_secs() < 60 { + backoff.saturating_add(Duration::from_secs(5)) + } else { + backoff + }; + + debug!("connect backoff: {} Seconds", b.as_secs()); + sleep(b).await; + b + } + + let _span = span!(Level::DEBUG, "ws_connect", server = self.service_address); + async move { + // Connect to the DID cache server + let mut backoff = Duration::from_secs(1); + loop { + debug!("Starting websocket connection"); + + let connection = connect_async(&self.service_address); + let timeout = tokio::time::sleep(self.config.network_timeout); + + select! { + conn = connection => { + match conn { + Ok((conn, _)) => { + debug!("Websocket connected"); + self.sdk_tx.send(WSCommands::Connected).await.unwrap(); + return Ok(conn) + } + Err(e) => { + error!("Error connecting to websocket: {:?}", e); + backoff = _handle_backoff(backoff).await; + } + } + } + _ = timeout => { + // Start backing off and retry + warn!("Connect timeout reached"); + backoff = _handle_backoff(backoff).await; + } + } + } + } + .instrument(_span) + .await + } + + /// Sends the request to the remote server via the websocket + async fn ws_send( + &self, + websocket: &mut WebSocketStream>, + request: &WSRequest, + ) -> Result<(), DIDCacheError> { + match websocket + .send(serde_json::to_string(request).unwrap().into()) + .await + { + Ok(_) => { + debug!("Request sent: {:?}", request); + Ok(()) + } + Err(e) => Err(DIDCacheError::TransportError(format!( + "Couldn't send request to network_task. Reason: {}", + e + ))), + } + } + + /// Processes inbound websocket messages from the remote server + fn ws_recv( + &mut self, + message: Option>, + ) -> Result<(), DIDCacheError> { + if let Some(response) = message { + match response { + Ok(msg) => { + if let Message::Text(msg) = msg { + let response: Result = serde_json::from_str(&msg); + match response { + Ok(WSResponseType::Response(response)) => { + debug!("Received response: {:?}", response.hash); + if let Some(channels) = self.cache.remove(&response.hash, None) { + // Loop through and notify each registered channel + for channel in channels { + let _ = channel.send(WSCommands::ResponseReceived( + Box::new(response.document.clone()), + )); + } + } else { + warn!("Response not found in request list: {}", response.hash); + } + } + Ok(WSResponseType::Error(response)) => { + warn!( + "Received error: did hash({}) Error: {:?}", + response.hash, response.error + ); + if let Some(channels) = self.cache.remove(&response.hash, None) { + for channel in channels { + let _ = channel.send(WSCommands::ErrorReceived( + response.error.clone(), + )); + } + } else { + warn!("Response not found in request list: {}", response.hash); + } + } + Err(e) => { + warn!("Error parsing message: {:?}", e); + } + } + } else { + warn!("Received non-text message, ignoring: {}", msg); + } + } + Err(e) => { + // Can't receive messages, reset the connection + error!("Error receiving message: {:?}", e); + return Err(DIDCacheError::TransportError(format!( + "Error receiving message: {:?}", + e + ))); + } + } + } else { + warn!("No message received"); + } + + Ok(()) + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/networking/request_queue.rs b/affinidi-did-resolver-cache-sdk/src/networking/request_queue.rs new file mode 100644 index 0000000..b803663 --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/networking/request_queue.rs @@ -0,0 +1,138 @@ +//! When messages are sent via websocket, the response may be out of order +//! [RequestList] helps manage the buffer and returns the right response + +use super::network::Responder; +use crate::config::ClientConfig; +use std::collections::HashMap; +use tracing::debug; + +/// List of lookups that are in progress.Note the list is not in any order. +/// NOTE: SHA256 Hash of the DID is used as the key for the list +/// - list: The list of requests waiting for a response from the server (key: DID Hash, value: Vec[(Unique ID, Responder Channel)] +/// - list_full: Is the list full based on limits? +/// - limit_count: The maximum number of items to store in the request list +/// - total_count: The total number of items in the list +/// +/// NOTE: Handles duplicate DID resolver requests, by matching them in the list by the DID hash, adds elements using +/// the unique ID as an identifier. +pub(crate) struct RequestList { + list: HashMap>, + list_full: bool, + limit_count: u32, + total_count: u32, +} + +impl RequestList { + /// Create a new request list + pub fn new(config: &ClientConfig) -> Self { + debug!( + "created request list limit_count({})", + config.network_cache_limit_count + ); + Self { + list: HashMap::new(), + list_full: false, + limit_count: config.network_cache_limit_count, + total_count: 0, + } + } + + /// Insert a new request into the list + /// Returns: true if the request is new, false if it is a duplicate (no need to send to server) + pub fn insert(&mut self, key: String, uid: &str, channel: Responder) -> bool { + // If the key exists, append the value to the list + if let Some(element) = self.list.get_mut(&key) { + element.push((uid.to_string(), channel)); + debug!( + "Duplicate resolver request, adding to queue to await response. id ({})", + key + ); + false + } else { + // Otherwise, create a new list with the value + self.list + .insert(key.clone(), vec![(uid.to_string(), channel)]); + + self.total_count += 1; + + if self.total_count > self.limit_count { + self.list_full = true; + } + + debug!( + "Request inserted: id({}) list_count({})", + key, self.total_count + ); + true + } + } + + /// Remove a response from the list returning the value + /// ^^ This is why we don't need a get() function... + /// If uid isn't provided, then all channels for given key are removed + /// If uid is provided, then we just remove that channel for that key (which if empty will delete the key) + pub(crate) fn remove(&mut self, key: &str, uid: Option) -> Option> { + // Get the Responder Channels from the list + // Request must be in the list itself! + + if let Some(uid) = uid { + let response = if let Some(channels) = self.list.get_mut(key) { + // Find the index of the element to remove + let index = channels.iter().position(|(id, _)| *id == uid); + + if let Some(index) = index { + // Remove the element from the list + let (_, channel) = channels.remove(index); + + debug!( + "Request removed: id({}) channels_waiting({}) list_count({})", + key, + channels.len(), + self.total_count + ); + Some(vec![channel]) + } else { + debug!("Request not found: id({}) unique_id({})", key, uid); + None + } + } else { + debug!("Request not found: id({})", key); + None + }; + + // If the list is empty, remove the key + if let Some(channels) = self.list.get(key) { + if channels.is_empty() { + self.list.remove(key); + self.total_count -= 1; + self.list_full = false; + } + } + + response + } else { + // Remove all channels for the key + if let Some(channels) = self.list.remove(key) { + self.total_count -= 1; + self.list_full = false; + + debug!( + "Request removed: hash({}) channels_waiting({}) remaining_list_count({})", + key, + channels.len(), + self.total_count + ); + + Some(channels.into_iter().map(|(_, channel)| channel).collect()) + } else { + debug!("Request not found: hash({})", key); + None + } + } + } + + /// Is the list full based on limits? + pub(crate) fn is_full(&self) -> bool { + self.list_full + } +} diff --git a/affinidi-did-resolver-cache-sdk/src/resolver/mod.rs b/affinidi-did-resolver-cache-sdk/src/resolver/mod.rs new file mode 100644 index 0000000..c73d2ee --- /dev/null +++ b/affinidi-did-resolver-cache-sdk/src/resolver/mod.rs @@ -0,0 +1,89 @@ +use did_peer::DIDPeer; +use ssi::dids::{DIDEthr, DIDKey, DIDResolver, DIDWeb, Document, DID, DIDJWK, DIDPKH}; +use tracing::error; + +use crate::{errors::DIDCacheError, DIDCacheClient}; + +impl DIDCacheClient { + /// Resolves a DID to a DID Document + pub(crate) async fn local_resolve( + &self, + did: &str, + parts: &[&str], + ) -> Result { + // Match the DID method + + match parts[1] { + "ethr" => { + let method = DIDEthr; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + "jwk" => { + let method = DIDJWK; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + "key" => { + let method = DIDKey; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + "peer" => { + let method = DIDPeer; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + "pkh" => { + let method = DIDPKH; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + "web" => { + let method = DIDWeb; + + match method.resolve(DID::new::(did).unwrap()).await { + Ok(res) => Ok(res.document.into_document()), + Err(e) => { + error!("Error: {:?}", e); + Err(DIDCacheError::DIDError(e.to_string())) + } + } + } + _ => Err(DIDCacheError::DIDError(format!( + "DID Method ({}) not supported", + parts[1] + ))), + } + } +} diff --git a/affinidi-did-resolver-cache-server/Cargo.toml b/affinidi-did-resolver-cache-server/Cargo.toml new file mode 100644 index 0000000..6f8bacb --- /dev/null +++ b/affinidi-did-resolver-cache-server/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "affinidi-did-resolver-cache-server" +version.workspace = true +description = "Affinidi DID Network Cache + Resolver Service" +edition.workspace = true +authors.workspace = true +readme.workspace = true +homepage.workspace = true +keywords.workspace = true +publish.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +affinidi-did-resolver-cache-sdk.workspace = true +axum.workspace = true +axum-server.workspace = true +blake2.workspace = true +chrono.workspace = true +did-peer.workspace = true +http.workspace = true +moka.workspace = true +rand.workspace = true +regex.workspace = true +serde.workspace = true +serde_json.workspace = true +ssi.workspace = true +thiserror.workspace = true +tokio.workspace = true +toml.workspace = true +tower-http.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true diff --git a/affinidi-did-resolver-cache-server/README.md b/affinidi-did-resolver-cache-server/README.md new file mode 100644 index 0000000..289e64b --- /dev/null +++ b/affinidi-did-resolver-cache-server/README.md @@ -0,0 +1,28 @@ +# Affinidi Trust Network - DID Universal Resolver Cache + +Provides local caching for DID resolution and caching of DID Documents + +This crate provides both a production running cache service and a SDK Library for client access to the ATN DID Universal Resolver. + +Clients are acting as a DID Document cache locally, all resolving is handled separately. +Benefit of using this crate is that all DID resolver methods can be shifted to a single service so that changes can be more easily deployed. + +## Prerequisites + +Rust version 1.79 + +## DID Universal Resolver Cache Service + +To run the resolver as a standalone network service, modify the `./conf/cache-conf.toml` configuration file, or set the ENV variables. + +`cargo run` will start the service, running in a production environment is beyond the scope of this crate. + +The service uses WebSockets for transport, operates a single service wide cache that if a DID lookup results in a hit miss, gets handed to a pool of resolvers for parallel resolving. Requests from clients can be multiplexed and may be responded to out of order, the client side is responsible for matching result to each request. + +## Client DID Document Cache + +The client side runs a local in-memory cache of DID Documents, if not known locally it will pass the request to the DID Universal Resolver Service and wait for a response. + +This implementation is thread-safe and can be cloned into multiple threads. + +To use ATN DID Universal Resolver Cache in other services, add this crate to your project Cargo.toml and utilize example code similar to: diff --git a/affinidi-did-resolver-cache-server/conf/cache-conf.toml b/affinidi-did-resolver-cache-server/conf/cache-conf.toml new file mode 100644 index 0000000..8ea846d --- /dev/null +++ b/affinidi-did-resolver-cache-server/conf/cache-conf.toml @@ -0,0 +1,20 @@ +### log_level: trace debug info warn error +### default: info +log_level = "info" + +### listen_address: : that this service will listen on. +### Default: 0.0.0.0:8080 +listen_address = "${LISTEN_ADDRESS:0.0.0.0:8080}" + +### statistics_interval: # of seconds between statistics logging +### Default: 60 second +statistics_interval = "${STATISTICS_INTERVAL:10}" + +[cache] +### capacity_count: Approx how many items to cache in memory +### Default: ~1,000 cached DID Documents +capacity_count = "${CACHE_CAPACITY_COUNT:1000}" + +### expire: Cache Time To Live (TTL) for a cached entry in seconds +### Default: 300 (5 minutes) +expire = "${EXPIRE:300}" diff --git a/affinidi-did-resolver-cache-server/src/common.rs b/affinidi-did-resolver-cache-server/src/common.rs new file mode 100644 index 0000000..3ec3e8a --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/common.rs @@ -0,0 +1,14 @@ +use rand::{distributions::Alphanumeric, Rng}; +use serde::{de::DeserializeOwned, Serialize}; + +/// Helps with deserializing the generic data field in the SuccessResponse struct +pub trait GenericDataStruct: DeserializeOwned + Serialize {} + +// Creates a random transaction identifier for each transaction +pub(crate) fn create_session_id() -> String { + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect() +} diff --git a/affinidi-did-resolver-cache-server/src/config.rs b/affinidi-did-resolver-cache-server/src/config.rs new file mode 100644 index 0000000..1725ee4 --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/config.rs @@ -0,0 +1,218 @@ +use crate::errors::CacheError; +use regex::{Captures, Regex}; +use serde::{Deserialize, Serialize}; +use std::{ + env, fmt, + fs::File, + io::{self, BufRead}, + path::Path, + time::Duration, +}; +use tracing::{event, level_filters::LevelFilter, Level}; +use tracing_subscriber::{reload::Handle, Registry}; + +#[derive(Debug, Serialize, Deserialize)] +struct CacheConfig { + #[serde(default)] + pub capacity_count: String, + #[serde(default)] + pub expire: String, +} + +impl Default for CacheConfig { + fn default() -> Self { + CacheConfig { + capacity_count: "1000".into(), + expire: "300".into(), + } + } +} + +/// ConfigRaw Struct is used to deserialize the configuration file +/// We then convert this to the CacheConfig Struct +#[derive(Debug, Serialize, Deserialize)] +struct ConfigRaw { + pub log_level: String, + pub listen_address: String, + pub statistics_interval: String, + pub cache: CacheConfig, +} + +pub struct Config { + pub log_level: LevelFilter, + pub listen_address: String, + pub statistics_interval: Duration, + pub cache_capacity_count: u32, + pub cache_expire: u32, +} + +impl fmt::Debug for Config { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Config") + .field("log_level", &self.log_level) + .field("listen_address", &self.listen_address) + .field( + "statistics_interval", + &format!("{} seconds", self.statistics_interval.as_secs()), + ) + .field("cache_capacity_count", &self.cache_capacity_count) + .field("cache_expire", &format!("{} seconds", self.cache_expire)) + .finish() + } +} + +impl Default for Config { + fn default() -> Self { + Config { + log_level: LevelFilter::INFO, + listen_address: "".into(), + statistics_interval: Duration::from_secs(60), + cache_capacity_count: CacheConfig::default() + .capacity_count + .parse() + .unwrap_or(1000), + cache_expire: CacheConfig::default().expire.parse().unwrap_or(300), + } + } +} + +impl TryFrom for Config { + type Error = CacheError; + + fn try_from(raw: ConfigRaw) -> Result { + Ok(Config { + log_level: match raw.log_level.as_str() { + "trace" => LevelFilter::TRACE, + "debug" => LevelFilter::DEBUG, + "info" => LevelFilter::INFO, + "warn" => LevelFilter::WARN, + "error" => LevelFilter::ERROR, + _ => LevelFilter::INFO, + }, + listen_address: raw.listen_address, + statistics_interval: Duration::from_secs(raw.statistics_interval.parse().unwrap_or(60)), + cache_capacity_count: raw.cache.capacity_count.parse().unwrap_or(1000), + cache_expire: raw.cache.expire.parse().unwrap_or(300), + }) + } +} + +/// Read the primary configuration file for the mediator +/// Returns a ConfigRaw struct, that still needs to be processed for additional information +/// and conversion to Config struct +fn read_config_file(file_name: &str) -> Result { + // Read configuration file parameters + event!(Level::INFO, "Config file({})", file_name); + let raw_config = read_file_lines(file_name)?; + + event!(Level::DEBUG, "raw_config = {:?}", raw_config); + let config_with_vars = expand_env_vars(&raw_config); + match toml::from_str(&config_with_vars.join("\n")) { + Ok(config) => Ok(config), + Err(err) => { + event!( + Level::ERROR, + "Could not parse configuration settings. {:?}", + err + ); + Err(CacheError::ConfigError( + "NA".into(), + format!("Could not parse configuration settings. Reason: {:?}", err), + )) + } + } +} + +/// Reads a file and returns a vector of strings, one for each line in the file. +/// It also strips any lines starting with a # (comments) +/// You can join the Vec back into a single string with `.join("\n")` +pub(crate) fn read_file_lines

(file_name: P) -> Result, CacheError> +where + P: AsRef, +{ + let file = File::open(file_name.as_ref()).map_err(|err| { + event!( + Level::ERROR, + "Could not open file({}). {}", + file_name.as_ref().display(), + err + ); + CacheError::ConfigError( + "NA".into(), + format!( + "Could not open file({}). {}", + file_name.as_ref().display(), + err + ), + ) + })?; + + let mut lines = Vec::new(); + for line in io::BufReader::new(file).lines().map_while(Result::ok) { + // Strip comments out + if !line.starts_with('#') { + lines.push(line); + } + } + + Ok(lines) +} + +/// Replaces all strings ${VAR_NAME:default_value} +/// with the corresponding environment variables (e.g. value of ${VAR_NAME}) +/// or with `default_value` if the variable is not defined. +fn expand_env_vars(raw_config: &Vec) -> Vec { + let re = Regex::new(r"\$\{(?P[A-Z_]{1,}[0-9A-Z_]*):(?P.*)\}").unwrap(); + let mut result: Vec = Vec::new(); + for line in raw_config { + result.push( + re.replace_all(line, |caps: &Captures| match env::var(&caps["env_var"]) { + Ok(val) => val, + Err(_) => (caps["default_value"]).into(), + }) + .into_owned(), + ); + } + result +} + +pub fn init(reload_handle: Option>) -> Result { + // Read configuration file parameters + let config = read_config_file("conf/cache-conf.toml")?; + + // Setup logging + if reload_handle.is_some() { + let level: LevelFilter = match config.log_level.as_str() { + "trace" => LevelFilter::TRACE, + "debug" => LevelFilter::DEBUG, + "info" => LevelFilter::INFO, + "warn" => LevelFilter::WARN, + "error" => LevelFilter::ERROR, + _ => { + event!( + Level::WARN, + "log_level({}) is unknown in config file. Defaults to INFO", + config.log_level + ); + LevelFilter::INFO + } + }; + reload_handle + .unwrap() + .modify(|filter| *filter = level) + .map_err(|e| CacheError::InternalError("NA".into(), e.to_string()))?; + event!(Level::INFO, "Log level set to ({})", config.log_level); + } + + match Config::try_from(config) { + Ok(config) => { + event!( + Level::INFO, + "Configuration settings parsed successfully.\n{:#?}", + config + ); + Ok(config) + } + Err(err) => Err(err), + } +} diff --git a/affinidi-did-resolver-cache-server/src/errors.rs b/affinidi-did-resolver-cache-server/src/errors.rs new file mode 100644 index 0000000..9e0a34c --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/errors.rs @@ -0,0 +1,167 @@ +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, + Json, +}; +use serde::{Deserialize, Serialize}; +use std::fmt; +use thiserror::Error; +use tracing::{event, Level}; + +use crate::common::GenericDataStruct; + +type SessId = String; + +pub struct AppError(CacheError); + +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} + +/// CacheError the first String is always the session_id +#[derive(Error, Debug)] +pub enum CacheError { + #[error("Error in handling errors! {1}")] + ErrorHandlingError(SessId, String), + #[error("{1}")] + InternalError(SessId, String), + #[error("Couldn't parse ({1}). Reason: {2}")] + ParseError(SessId, String, String), + #[error("DID Error: did({1}) Error: {2}")] + DIDError(SessId, String, String), + #[error("Configuration Error: {1}")] + ConfigError(SessId, String), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + let response = match self.0 { + CacheError::ErrorHandlingError(session_id, msg) => { + let response = ErrorResponse { + httpCode: StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + sessionId: session_id.to_string(), + errorCode: 1, + errorCodeStr: "ErrorHandlingError".to_string(), + message: msg.to_string(), + }; + event!(Level::WARN, "{}", response.to_string()); + response + } + CacheError::InternalError(session_id, msg) => { + let response = ErrorResponse { + httpCode: StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + sessionId: session_id.to_string(), + errorCode: 2, + errorCodeStr: "InternalError".to_string(), + message: msg.to_string(), + }; + event!(Level::WARN, "{}", response.to_string()); + response + } + CacheError::ParseError(session_id, _, msg) => { + let response = ErrorResponse { + httpCode: StatusCode::BAD_REQUEST.as_u16(), + sessionId: session_id.to_string(), + errorCode: 3, + errorCodeStr: "BadRequest: ParseError".to_string(), + message: msg.to_string(), + }; + event!(Level::WARN, "{}", response.to_string()); + response + } + CacheError::DIDError(session_id, did, msg) => { + let response = ErrorResponse { + httpCode: StatusCode::BAD_REQUEST.as_u16(), + sessionId: session_id.to_string(), + errorCode: 8, + errorCodeStr: "DIDError".to_string(), + message: format!("did({}) Error: {}", did, msg), + }; + event!(Level::WARN, "{}", response.to_string()); + response + } + CacheError::ConfigError(session_id, message) => { + let response = ErrorResponse { + httpCode: StatusCode::SERVICE_UNAVAILABLE.as_u16(), + sessionId: session_id.to_string(), + errorCode: 9, + errorCodeStr: "ConfigError".to_string(), + message, + }; + event!(Level::WARN, "{}", response.to_string()); + response + } + }; + ( + StatusCode::from_u16(response.httpCode).ok().unwrap(), + Json(response), + ) + .into_response() + } +} + +#[derive(Serialize, Debug)] +#[allow(non_snake_case)] +pub struct ErrorResponse { + pub sessionId: String, + pub httpCode: u16, + pub errorCode: u16, + pub errorCodeStr: String, + pub message: String, +} + +impl fmt::Display for ErrorResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}: httpcode({}) errorCode({}), errorCodeStr({}) message({})", + self.sessionId, self.httpCode, self.errorCode, self.errorCodeStr, self.message, + ) + } +} +#[derive(Serialize, Deserialize, Debug)] +#[allow(non_snake_case)] +pub struct SuccessResponse { + pub sessionId: String, + pub httpCode: u16, + pub errorCode: i32, + pub errorCodeStr: String, + pub message: String, + #[serde(bound(deserialize = ""))] + pub data: Option, +} + +impl fmt::Display for SuccessResponse { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}: httpcode({}) errorCode({}), errorCodeStr({}) message({})", + self.sessionId, self.httpCode, self.errorCode, self.errorCodeStr, self.message, + ) + } +} + +impl SuccessResponse { + pub fn response( + session_id: &str, + http_code: StatusCode, + msg: &str, + data: Option, + ) -> Json> { + let response = SuccessResponse { + sessionId: session_id.to_string(), + httpCode: http_code.as_u16(), + errorCode: 0, + errorCodeStr: "Ok".to_string(), + message: msg.to_string(), + data, + }; + event!(Level::INFO, "{response}"); + Json(response) + } +} diff --git a/affinidi-did-resolver-cache-server/src/handlers/mod.rs b/affinidi-did-resolver-cache-server/src/handlers/mod.rs new file mode 100644 index 0000000..d6335d1 --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/handlers/mod.rs @@ -0,0 +1,29 @@ +use crate::SharedData; +use axum::{extract::State, response::IntoResponse, routing::get, Json, Router}; + +pub(crate) mod websocket; + +pub fn application_routes(shared_data: &SharedData) -> Router { + let app = Router::new() + // Inbound message handling from ATM clients + // Websocket endpoint for clients + .route("/ws", get(websocket::websocket_handler)); + + Router::new() + .nest("/did/v1/", app) + .with_state(shared_data.to_owned()) +} + +pub async fn health_checker_handler(State(state): State) -> impl IntoResponse { + let message: String = format!( + "Affinidi Trust Network - DID Cache, Version: {}, Started: UTC {}", + env!("CARGO_PKG_VERSION"), + state.service_start_timestamp.format("%Y-%m-%d %H:%M:%S"), + ); + + let response_json = serde_json::json!({ + "status": "success".to_string(), + "message": message, + }); + Json(response_json) +} diff --git a/affinidi-did-resolver-cache-server/src/handlers/websocket.rs b/affinidi-did-resolver-cache-server/src/handlers/websocket.rs new file mode 100644 index 0000000..a90198b --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/handlers/websocket.rs @@ -0,0 +1,113 @@ +use affinidi_did_resolver_cache_sdk::networking::{ + WSRequest, WSResponse, WSResponseError, WSResponseType, +}; +use axum::{ + extract::{ + ws::{Message, WebSocket}, + State, WebSocketUpgrade, + }, + response::IntoResponse, +}; +use tokio::select; +use tracing::{debug, info, span, warn, Instrument}; + +use crate::SharedData; + +// Handles the switching of the protocol to a websocket connection +pub async fn websocket_handler( + //session: Session, + ws: WebSocketUpgrade, + State(state): State, +) -> impl IntoResponse { + let _span = span!( + tracing::Level::DEBUG, + "websocket_handler", + // session = session.session_id + ); + /*async move { ws.on_upgrade(move |socket| handle_socket(socket, state, session)) } + .instrument(_span) + .await*/ + + async move { ws.on_upgrade(move |socket| handle_socket(socket, state)) } + .instrument(_span) + .await +} + +/// WebSocket state machine. This is spawned per connection. +//async fn handle_socket(mut socket: WebSocket, state: SharedData, session: Session) { +async fn handle_socket(mut socket: WebSocket, state: SharedData) { + let _span = span!( + tracing::Level::DEBUG, + "handle_socket", + //session = session.session_id + ); + async move { + state.stats().await.increment_ws_opened(); + info!("Websocket connection established"); + + loop { + select! { + value = socket.recv() => { + if let Some(msg) = value { + if let Ok(msg) = msg { + if let Message::Text(msg) = msg { + debug!("ws: Received text message: {:?}", msg); + let request: WSRequest = match serde_json::from_str(&msg) { + Ok(request) => request, + Err(e) => { + warn!("ws: Error parsing message: {:?}", e); + break; + } + }; + + match state.resolver.resolve(&request.did).await { + Ok(response) => { + let message = WSResponseType::Response(WSResponse { + did: response.did.clone(), + hash: response.did_hash, + document: response.doc, + }); + let mut stats = state.stats().await; + stats.increment_resolver_success(); + if response.cache_hit { stats.increment_cache_hit();} + stats.increment_did_method_success(response.method); + drop(stats); + info!("resolved DID: ({}) cache_hit?({})", response.did, response.cache_hit); + if let Err(e) = socket.send(Message::Text(serde_json::to_string(&message).unwrap())).await { + warn!("ws: Error sending response: {:?}", e); + break; + } else { + debug!("Sent response: {:?}", message); + } + } + Err(e) => { + // Couldn't resolve the DID, send an error back + warn!("Couldn't resolve DID: ({}) Reason: {}", &request.did, e); + state.stats().await.increment_resolver_error(); + if let Err(e) = socket.send(Message::Text(serde_json::to_string(&WSResponseType::Error(WSResponseError {did: request.did, hash: request.hash, error: e.to_string()})).unwrap())).await { + warn!("ws: Error sending error response: {:?}", e); + break; + } + } + } + } else { + warn!("Received non-text message, ignoring"); + continue; + } + } + } else { + debug!("Received None, closing connection"); + break; + } + } + } + } + + // We're done, close the connection + state.stats().await.increment_ws_closed(); + + info!("Websocket connection closed"); + } + .instrument(_span) + .await +} diff --git a/affinidi-did-resolver-cache-server/src/lib.rs b/affinidi-did-resolver-cache-server/src/lib.rs new file mode 100644 index 0000000..608d750 --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/lib.rs @@ -0,0 +1,45 @@ +use std::{fmt::Debug, sync::Arc}; + +use affinidi_did_resolver_cache_sdk::DIDCacheClient; +use axum::{ + async_trait, + extract::{FromRef, FromRequestParts}, + http::request::Parts, +}; +use chrono::{DateTime, Utc}; +use session::SessionError; +use statistics::Statistics; +use tokio::sync::{Mutex, MutexGuard}; + +pub(crate) mod common; +pub mod config; +pub mod errors; +pub mod handlers; +pub mod session; +pub mod statistics; + +#[derive(Clone)] +pub struct SharedData { + pub service_start_timestamp: DateTime, + pub stats: Arc>, + pub resolver: DIDCacheClient, +} + +#[async_trait] +impl FromRequestParts for SharedData +where + Self: FromRef, + S: Send + Sync + Debug, +{ + type Rejection = SessionError; + + async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result { + Ok(Self::from_ref(state)) + } +} + +impl SharedData { + pub async fn stats(&self) -> MutexGuard { + self.stats.lock().await + } +} diff --git a/affinidi-did-resolver-cache-server/src/main.rs b/affinidi-did-resolver-cache-server/src/main.rs new file mode 100644 index 0000000..1f4cf02 --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/main.rs @@ -0,0 +1,135 @@ +use affinidi_did_resolver_cache_sdk::{ + config::ClientConfigBuilder, errors::DIDCacheError, DIDCacheClient, +}; +use affinidi_did_resolver_cache_server::{ + config::init, + handlers::{application_routes, health_checker_handler}, + statistics::{statistics, Statistics}, + SharedData, +}; +use axum::{routing::get, Router}; +use http::Method; +use std::{env, net::SocketAddr, sync::Arc}; +use tokio::sync::Mutex; +use tower_http::{ + cors::CorsLayer, + trace::{self, TraceLayer}, +}; +use tracing::{event, Level}; +use tracing_subscriber::{filter, layer::SubscriberExt, reload, util::SubscriberInitExt}; + +/// Main entry point for the `affinidi-did-resolver-cache` binary. +/// +/// This binary runs as a DID Cache resolver service with built in caching +#[tokio::main] +async fn main() -> Result<(), DIDCacheError> { + // setup logging/tracing framework + let filter = filter::LevelFilter::INFO; // This can be changed in the config file! + let (filter, reload_handle) = reload::Layer::new(filter); + let ansi = env::var("LOCAL").is_ok(); + tracing_subscriber::registry() + .with(filter) + .with(tracing_subscriber::fmt::layer().with_ansi(ansi)) + .init(); + + if ansi { + event!(Level::INFO, ""); + event!( + Level::INFO, + r#" db 888888888888 888b 88 88888888ba, 88 88888888ba, ,ad8888ba, 88 "# + ); + event!( + Level::INFO, + r#" d88b 88 8888b 88 88 `"8b 88 88 `"8b d8"' `"8b 88 "# + ); + event!( + Level::INFO, + r#" d8'`8b 88 88 `8b 88 88 `8b 88 88 `8b d8' 88 "# + ); + event!( + Level::INFO, + r#" d8' `8b 88 88 `8b 88 88 88 88 88 88 88 ,adPPYYba, ,adPPYba, 88,dPPYba, ,adPPYba, "# + ); + event!( + Level::INFO, + r#" d8YaaaaY8b 88 88 `8b 88 88 88 88 88 88 88 "" `Y8 a8" "" 88P' "8a a8P_____88 "# + ); + event!( + Level::INFO, + r#" d8""""""""8b 88 88 `8b 88 88 8P 88 88 8P Y8, ,adPPPPP88 8b 88 88 8PP""""""" "# + ); + event!( + Level::INFO, + r#" d8' `8b 88 88 `8888 88 .a8P 88 88 .a8P Y8a. .a8P 88, ,88 "8a, ,aa 88 88 "8b, ,aa "# + ); + event!( + Level::INFO, + r#" d8' `8b 88 88 `888 88888888Y"' 88 88888888Y"' `"Y8888Y"' `"8bbdP"Y8 `"Ybbd8"' 88 88 `"Ybbd8"' "# + ); + event!(Level::INFO, ""); + } + + event!(Level::INFO, "[Loading Affinidi DID Cache configuration]"); + + let config = init(Some(reload_handle)).expect("Couldn't initialize DID Cache!"); + + // Use the affinidi-did-resolver-cache-sdk in local mode + let cache_config = ClientConfigBuilder::default() + .with_cache_capacity(config.cache_capacity_count) + .with_cache_ttl(config.cache_expire) + .build(); + + let resolver = DIDCacheClient::new(cache_config).await?; + + // Create the shared application State + let shared_state = SharedData { + service_start_timestamp: chrono::Utc::now(), + stats: Arc::new(Mutex::new(Statistics::default())), + resolver, + }; + + // Start the statistics thread + let _stats = shared_state.stats.clone(); + let _cache = shared_state.resolver.get_cache(); + tokio::spawn(async move { + statistics(config.statistics_interval, &_stats, _cache) + .await + .expect("Error starting statistics thread"); + }); + + // build our application routes + let app: Router = application_routes(&shared_state); + + // Add middleware to all routes + let app = Router::new() + .merge(app) + .layer( + CorsLayer::new() + .allow_origin(tower_http::cors::Any) + .allow_headers([http::header::CONTENT_TYPE]) + .allow_methods([ + Method::GET, + Method::POST, + Method::PUT, + Method::DELETE, + Method::PATCH, + ]), + ) + .layer( + TraceLayer::new_for_http() + .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) + .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), + ) + // Add the healthcheck route after the tracing so we don't fill up logs with healthchecks + .route( + "/did/healthchecker", + get(health_checker_handler).with_state(shared_state), + ); + + axum_server::bind(config.listen_address.parse().unwrap()) + .serve(app.into_make_service_with_connect_info::()) + .await + .unwrap(); + + Ok(()) +} diff --git a/affinidi-did-resolver-cache-server/src/session.rs b/affinidi-did-resolver-cache-server/src/session.rs new file mode 100644 index 0000000..091de5f --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/session.rs @@ -0,0 +1,89 @@ +use crate::{common::create_session_id, errors::ErrorResponse, SharedData}; +use axum::{ + async_trait, + extract::{FromRef, FromRequestParts}, + http::{request::Parts, StatusCode}, + response::{IntoResponse, Response}, + Json, +}; +use serde::Serialize; +use serde_json::json; +use std::{ + fmt::{Debug, Display}, + net::SocketAddr, +}; +use tracing::{info, warn}; + +#[derive(Debug)] +pub enum SessionError { + SessionError(String), +} + +impl Display for SessionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SessionError::SessionError(message) => { + write!(f, "Error: {}", message) + } + } + } +} + +impl IntoResponse for SessionError { + fn into_response(self) -> Response { + let status = match self { + SessionError::SessionError(_) => StatusCode::BAD_REQUEST, + }; + let body = Json(json!(ErrorResponse { + sessionId: "UNAUTHORIZED".into(), + httpCode: status.as_u16(), + errorCode: status.as_u16(), + errorCodeStr: status.to_string(), + message: self.to_string(), + })); + (status, body).into_response() + } +} + +#[derive(Clone, Debug, Serialize)] +pub struct Session { + pub session_id: String, // Unique session transaction ID + pub remote_addr: String, // Remote Socket address +} + +#[async_trait] +impl FromRequestParts for Session +where + SharedData: FromRef, + S: Send + Sync + Debug, +{ + type Rejection = SessionError; + async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { + let remote_addr = if let Some(address) = parts + .extensions + .get::>() + .map(|ci| ci.0) + { + address.to_string() + } else { + warn!("No remote address in request!"); + return Err(SessionError::SessionError( + "No remote address in request!".into(), + )); + }; + + let session_id = create_session_id(); + + info!( + "{}: Connection accepted from address({})", + &session_id, &remote_addr + ); + + let session = Session { + session_id, + remote_addr, + }; + + Ok(session) + } +} diff --git a/affinidi-did-resolver-cache-server/src/statistics.rs b/affinidi-did-resolver-cache-server/src/statistics.rs new file mode 100644 index 0000000..92c47d2 --- /dev/null +++ b/affinidi-did-resolver-cache-server/src/statistics.rs @@ -0,0 +1,156 @@ +//! Statistics module for the cache server. +//! Creates a parallel task that logs cache statistics based on an interval +use crate::errors::CacheError; +use affinidi_did_resolver_cache_sdk::DIDMethod; +use moka::future::Cache; +use ssi::dids::Document; +use std::{ + collections::HashMap, + fmt::{self, Display, Formatter}, + sync::Arc, + time::Duration, +}; +use tokio::sync::Mutex; +use tracing::{debug, info, span, Instrument, Level}; + +/// Statistics struct for the cache server +/// Contains information about the cache, websocket connections, and resolver requests +/// ws_opened: number of opened websocket connections +/// ws_closed: number of closed websocket connections +/// cache_size: number of entries in the cache (approximate) +/// resolver_success: number of successful resolver requests +/// resolver_error: number of failed resolver requests +/// cache_hit: number of cache hits (calculate as a % against resolver_success) +/// method: number of resolver requests per DID method (success) +#[derive(Clone, Debug, Default)] +pub struct Statistics { + ws_opened: i64, + ws_closed: i64, + cache_size: i64, + resolver_success: u64, + resolver_error: u64, + cache_hit: u64, + method: HashMap, +} + +impl Display for Statistics { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // protects against division by zero + debug!( + "Calculating cache hit rate success {}", + self.resolver_success + ); + let cache_hit_rate = if self.resolver_success > 0 { + (self.cache_hit as f64 / self.resolver_success as f64) * 100.0 + } else { + 0.0 + }; + + write!( + f, + r#" + Cache: count({}) Hits({} {:.2}%) + Connections: ws_open({}) ws_close({}) ws_current({}) + Resolver: total({}) success({}) error({}) + Methods (METHOD: COUNT): {} + "#, + self.cache_size, + self.cache_hit, + cache_hit_rate, + self.ws_opened, + self.ws_closed, + self.ws_opened - self.ws_closed, + self.resolver_success + self.resolver_error, + self.resolver_success, + self.resolver_error, + self.method + .iter() + .map(|(k, v)| format!("({}: {})", k, v)) + .collect::>() + .join(", ") + ) + } +} + +impl Statistics { + pub(crate) fn delta(&self, previous: &Statistics) -> Statistics { + Statistics { + ws_opened: self.ws_opened - previous.ws_opened, + ws_closed: self.ws_closed - previous.ws_closed, + cache_size: self.cache_size - previous.cache_size, + resolver_success: self.resolver_success - previous.resolver_success, + resolver_error: self.resolver_error - previous.resolver_error, + cache_hit: self.cache_hit - previous.cache_hit, + method: self + .method + .iter() + .map(|(k, v)| (k.clone(), v - previous.method.get(k).unwrap_or(&(0)))) + .collect(), + } + } + + /// Increments the number of opened websocket connections + pub fn increment_ws_opened(&mut self) { + self.ws_opened += 1; + } + + /// Increments the number of closed websocket connections + pub fn increment_ws_closed(&mut self) { + self.ws_closed += 1; + } + + /// Increments the number of successful resolver requests + pub fn increment_resolver_success(&mut self) { + self.resolver_success += 1; + } + + /// Increments the number of failed resolver requests + pub fn increment_resolver_error(&mut self) { + self.resolver_error += 1; + } + + /// Increments the number of cache hits + pub fn increment_cache_hit(&mut self) { + self.cache_hit += 1; + } + + /// Increments the number of successful resolver requests for a specific DID method + pub fn increment_did_method_success(&mut self, method: DIDMethod) { + self.method + .entry(method) + .and_modify(|v| *v += 1) + .or_insert(0); + } +} + +/// Periodically logs statistics about the cache. +/// Is spawned as a task from main(). +pub async fn statistics( + interval: Duration, + stats: &Arc>, + cache: Cache, +) -> Result<(), CacheError> { + let _span = span!(Level::INFO, "statistics"); + + async move { + debug!("Starting statistics thread..."); + let mut wait = tokio::time::interval(interval); + + let mut previous_stats = Statistics::default(); + loop { + wait.tick().await; + + let mut stats = stats.lock().await; + + cache.run_pending_tasks().await; + stats.cache_size = cache.entry_count() as i64; + + info!("Statistics: {}", stats); + info!("Delta: {}", stats.delta(&previous_stats)); + + previous_stats = stats.clone(); + } + } + .instrument(_span) + .await +} diff --git a/affinidi-did-resolver-methods/Cargo.toml b/affinidi-did-resolver-methods/Cargo.toml new file mode 100644 index 0000000..8ca7539 --- /dev/null +++ b/affinidi-did-resolver-methods/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "affinidi-did-resolver-methods" +version.workspace = true +description = "Affinidi DID Methods" +edition.workspace = true +authors.workspace = true +readme.workspace = true +homepage.workspace = true +keywords.workspace = true +publish.workspace = true +license.workspace = true +repository.workspace = true diff --git a/affinidi-did-resolver-methods/did-peer/CHANGELOG b/affinidi-did-resolver-methods/did-peer/CHANGELOG new file mode 100644 index 0000000..5e42e55 --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/CHANGELOG @@ -0,0 +1,31 @@ +# Affinidi DID-Peer ChangeLog + +## Release 0.4.1 - 5th September 2024 + +- Added Purpose Codes for did:peer:2 + - A : Assertion Method + - D : Capability Delegation + - I : Capability Invocation +- Added support for did:peer type 0 (effectively translates did:peer:0(id) to a did:key:(id)) + +## Release 0.4.0 - 28th July 2024 + +- Refactored to support SSI v0.8 crate +- Fixed old crate dependencies caused by SSI v0.7 crate +- Fixed type definitions on verificationMethod to not use `multikey` + +## Release 0.3.1 - 16th May 2024 + +- Ensured every unwrap() is handled so no panics. + +## Release 0.3.0 - 27th April 2024 + +- Added example website with full WASM integration with resolve_did_peer() and create_did_peer() +- Changed the DIDPeerCreateKeys struct so that you can specify key type (supports ed25519, p256 and secp256k1) +- Cleaned up WASM definitions and bindings + +## Release 0.2.0 - 21st April 2024 + +- Added WASM bind for resolve_did_peer() +- Changed create_did_peer() input and return results so it can support auto-generating key values and return private key info +- Documentation for WASM structs and functions provided diff --git a/affinidi-did-resolver-methods/did-peer/Cargo.toml b/affinidi-did-resolver-methods/did-peer/Cargo.toml new file mode 100644 index 0000000..c28fbcf --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "did-peer" +version.workspace = true +description = "Implementation of the did:peer method in Rust, uses the ssi crate" +repository.workspace = true +edition.workspace = true +authors.workspace = true +readme.workspace = true +homepage.workspace = true +keywords.workspace = true +publish.workspace = true +license.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +base64.workspace = true +iref.workspace = true +serde_json.workspace = true +serde.workspace = true +serde-wasm-bindgen.workspace = true +ssi.workspace = true +thiserror.workspace = true +wasm-bindgen.workspace = true +wasm-bindgen-futures.workspace = true + +[dev-dependencies] +askar-crypto.workspace = true +tokio.workspace = true +multibase.workspace = true + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" +lto = true diff --git a/affinidi-did-resolver-methods/did-peer/LICENCE-APACHE b/affinidi-did-resolver-methods/did-peer/LICENCE-APACHE new file mode 100644 index 0000000..d51557b --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/LICENCE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2024] [Affinidi Pte. Ltd.] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/affinidi-did-resolver-methods/did-peer/README.md b/affinidi-did-resolver-methods/did-peer/README.md new file mode 100644 index 0000000..2879e36 --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/README.md @@ -0,0 +1,26 @@ +# did-peer Rust implementation + +Only supports did:peer numalgo 0,2 (did:peer:0, did:peer:2) + +## Build a WebAssembly package + +**Prerequisite:** [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) + +`wasm-pack build --target web --out-dir www/pkg` + +this places compiled wasm files into the ./www/pkg/ directory. + +Serve the website locally, run from the ./www/ directory of the project +`python3 -m http.server` + +Website available [here](http://127.0.0.1:8000) + +## Examples + +To run examples `cargo run --example ` + +Generate a random did:peer and corresponding DID document +`cargo run --example generate` + +Resolve a did:peer to a DID Document +`cargo run --example resolve ` diff --git a/affinidi-did-resolver-methods/did-peer/examples/generate.rs b/affinidi-did-resolver-methods/did-peer/examples/generate.rs new file mode 100644 index 0000000..3cf3d8f --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/examples/generate.rs @@ -0,0 +1,152 @@ +use did_peer::{ + DIDPeer, DIDPeerCreateKeys, DIDPeerKeys, DIDPeerService, PeerServiceEndPoint, + PeerServiceEndPointLong, +}; +use ssi::{ + dids::{DIDResolver, DID}, + jwk::Params, + multicodec::MultiEncoded, + JWK, +}; + +#[tokio::main] +async fn main() { + // Generate keys for encryption and verification + let e_ed25519_key = JWK::generate_ed25519().unwrap(); + let v_ed25519_key = JWK::generate_ed25519().unwrap(); + let v_secp256k1_key = JWK::generate_secp256k1(); + + // Print the private keys in case you want to save them for later + println!("Private keys:"); + if let Params::OKP(map) = e_ed25519_key.clone().params { + println!( + "E: private-key (d): {} {}", + map.curve, + String::from(map.private_key.clone().unwrap()) + ); + println!( + "E: public-key (x): {} {}", + map.curve, + String::from(map.public_key.clone()) + ); + } + println!(); + + if let Params::OKP(map) = v_ed25519_key.clone().params { + println!( + "V: private-key (d): {} {}", + map.curve, + String::from(map.private_key.clone().unwrap()) + ); + println!( + "V: public-key (x): {} {}", + map.curve, + String::from(map.public_key.clone()) + ); + } + println!(); + + if let Params::EC(map) = v_secp256k1_key.clone().params { + println!( + "V2: private-key: {} {}", + map.curve.clone().unwrap(), + String::from(map.ecc_private_key.clone().unwrap()) + ); + println!( + "V2: public-key (x): {} {}", + map.curve.clone().unwrap(), + String::from(map.x_coordinate.clone().unwrap()) + ); + println!( + "V2: public-key (y): {} {}", + map.curve.clone().unwrap(), + String::from(map.y_coordinate.clone().unwrap()) + ); + } + println!(); + + let key = ssi::dids::DIDKey; + + // Create the did:key DID's for each key above + let e_did_key = ssi::dids::DIDKey::generate(&e_ed25519_key).unwrap(); + let v_did_key = ssi::dids::DIDKey::generate(&v_ed25519_key).unwrap(); + let v2_did_key = ssi::dids::DIDKey::generate(&v_secp256k1_key).unwrap(); + + // Put these keys in order and specify the type of each key (we strip the did:key: from the front) + let keys = vec![ + DIDPeerCreateKeys { + purpose: DIDPeerKeys::Verification, + type_: None, + public_key_multibase: Some(v_did_key[8..].to_string()), + }, + DIDPeerCreateKeys { + purpose: DIDPeerKeys::Encryption, + type_: None, + public_key_multibase: Some(e_did_key[8..].to_string()), + }, + DIDPeerCreateKeys { + purpose: DIDPeerKeys::Encryption, + type_: None, + public_key_multibase: Some(v2_did_key[8..].to_string()), + }, + ]; + + // Create a service definition + let services = vec![DIDPeerService { + _type: "dm".into(), + service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong { + uri: "https://localhost:7037/".into(), + accept: vec!["didcomm/v2".into()], + routing_keys: vec![], + }), + id: None, + }]; + + // Create the did:peer DID + let (did_peer, _) = + DIDPeer::create_peer_did(&keys, Some(&services)).expect("Failed to create did:peer"); + + println!("{}", did_peer); + + println!(); + + // Resolve the did:peer DID to a Document + let peer = DIDPeer; + + let output = match peer.resolve(DID::new::(&did_peer).unwrap()).await { + Ok(res) => res, + Err(e) => { + println!("Error: {:?}", e); + return; + } + }; + + println!( + "DID Document:\n{}", + serde_json::to_string_pretty(&output.document).unwrap() + ); + println!("Metadata: {:?}", output.metadata); + + println!(); + println!("Expand keys"); + let expanded = DIDPeer::expand_keys(&output.document).await; + println!( + "DID Document:\n{}", + serde_json::to_string_pretty(&expanded.unwrap()).unwrap() + ); + + let output = key + .resolve(DID::new("did:key:z6Mkp89diy1PZkbUBDTpiqZBotddb1VV7JnY8qiZMGErUbFe").unwrap()) + .await + .unwrap(); + + println!("key :\n{:#?}", output.document); + + let a = multibase::decode("z6Mkp89diy1PZkbUBDTpiqZBotddb1VV7JnY8qiZMGErUbFe").unwrap(); + println!("{:?}", a); + let b = MultiEncoded::new(&a.1).unwrap(); + let jwk = JWK::from_multicodec(b).unwrap(); + println!("{}", jwk); + + //let jwk = JWK::from +} diff --git a/affinidi-did-resolver-methods/did-peer/examples/resolve.rs b/affinidi-did-resolver-methods/did-peer/examples/resolve.rs new file mode 100644 index 0000000..17a39a9 --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/examples/resolve.rs @@ -0,0 +1,32 @@ +use std::env; + +use did_peer::DIDPeer; +use ssi::dids::{DIDResolver, DID}; + +#[tokio::main] +async fn main() { + let args: Vec = env::args().collect(); + + // First argument is the command executed, second is the DID to parse + if args.len() != 2 { + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + + // Resolve the did:peer DID to a Document + let peer = DIDPeer; + + let output = match peer.resolve(DID::new::(&args[1]).unwrap()).await { + Ok(res) => res, + Err(e) => { + println!("Error: {:?}", e); + return; + } + }; + + println!( + "DID Document:\n{}", + serde_json::to_string_pretty(&output.document).unwrap() + ); + println!("Metadata: {:?}", output.metadata); +} diff --git a/affinidi-did-resolver-methods/did-peer/src/lib.rs b/affinidi-did-resolver-methods/did-peer/src/lib.rs new file mode 100644 index 0000000..dae66ce --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/src/lib.rs @@ -0,0 +1,894 @@ +//! # DID Peer Method +//! +//! The `did-peer` method is a DID method that is designed to be used for peer-to-peer communication. +//! It is based on did:key which can be used for Verification (V) and Encryption (E) purposes. +//! It also supports services (S) which can be used to define endpoints for communication. +//! +//! Example: +//! ```ignore +//! let peer = DIDPeer; +//! match peer.resolve(DID::new::("did:peer:2.Vabc...").unwrap()).await { +//! Ok(res) => { +//! println!("DID DOcument: {:#?}", res.document.into_document()), +//! }, +//! Err(e) => { +//! println!("Error: {:?}", e); +//! } +//! } +//! ``` +//! +use base64::prelude::*; +use iref::UriBuf; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use ssi::{ + dids::{ + document::{ + self, + representation::{self, MediaType}, + service::Endpoint, + verification_method, DIDVerificationMethod, Service, VerificationRelationships, + }, + key::VerificationMethodType, + resolution::{self, Error, Options, Output}, + DIDBuf, DIDKey, DIDMethod, DIDMethodResolver, DIDURLBuf, DIDURLReferenceBuf, Document, + RelativeDIDURLBuf, DID, + }, + jwk::Params, + prelude::*, + OneOrMany, +}; +use std::{collections::BTreeMap, fmt}; +use thiserror::Error; +use wasm_bindgen::prelude::*; + +#[derive(Error, Debug)] +pub enum DIDPeerError { + #[error("Unsupported key type")] + UnsupportedKeyType, + #[error("Unsupported curve: {0}")] + UnsupportedCurve(String), + #[error("Unsupported source")] + UnsupportedSource, + #[error("Syntax error on Service definition: {0}")] + SyntaxErrorServiceDefinition(String), + #[error("Unsupported Method. Must be method 2")] + MethodNotSupported, + #[error("Key Parsing error {0}")] + KeyParsingError(String), + #[error("DID Document doesn't contain any verificationMethod items")] + MissingVerificationMethods, + #[error("JSON Parsing error: {0}")] + JsonParsingError(String), + #[error("Internal error: {0}")] + InternalError(String), +} + +// Converts DIDPeerError to JsValue which is required for propagating errors to WASM +impl From for JsValue { + fn from(err: DIDPeerError) -> JsValue { + JsValue::from(err.to_string()) + } +} + +pub struct DIDPeer; + +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +pub enum PeerServiceEndPoint { + Short(PeerServiceEndPointShort), + Long(PeerServiceEndPointLong), +} + +impl PeerServiceEndPoint { + pub fn to_short(&self) -> PeerServiceEndPointShort { + match self { + PeerServiceEndPoint::Short(short) => short.clone(), + PeerServiceEndPoint::Long(long) => PeerServiceEndPointShort { + uri: long.uri.clone(), + a: long.accept.clone(), + r: long.routing_keys.clone(), + }, + } + } + + pub fn to_long(&self) -> PeerServiceEndPointLong { + match self { + PeerServiceEndPoint::Short(short) => PeerServiceEndPointLong::from(short.clone()), + PeerServiceEndPoint::Long(long) => long.clone(), + } + } +} + +/// DID serviceEndPoint structure in short format +#[derive(Clone, Serialize, Deserialize)] +pub struct PeerServiceEndPointShort { + pub uri: String, + pub a: Vec, + pub r: Vec, +} + +/// DID serviceEndPoint structure in long format +#[derive(Clone, Serialize, Deserialize)] +pub struct PeerServiceEndPointLong { + pub uri: String, + pub accept: Vec, + pub routing_keys: Vec, +} + +impl From for PeerServiceEndPointLong { + fn from(service: PeerServiceEndPointShort) -> Self { + PeerServiceEndPointLong { + uri: service.uri, + accept: service.a, + routing_keys: service.r, + } + } +} +/// DID Service structure in abbreviated format +#[derive(Serialize, Deserialize)] +pub struct DIDPeerService { + #[serde(rename = "t")] + #[serde(alias = "t")] + pub _type: String, + #[serde(rename = "s")] + #[serde(alias = "s")] + pub service_end_point: PeerServiceEndPoint, // serviceEndPoint + pub id: Option, +} + +impl From for Service { + fn from(service: DIDPeerService) -> Self { + let service_endpoint = + match serde_json::to_value(PeerServiceEndPoint::to_long(&service.service_end_point)) { + Ok(value) => Some(OneOrMany::One(Endpoint::Map(value))), + Err(_) => None, + }; + + let id = if let Some(id) = service.id { + UriBuf::new(id.into()).unwrap() + } else { + UriBuf::new("did:peer:#service".into()).unwrap() + }; + + Service { + id, + type_: OneOrMany::One("DIDCommMessaging".into()), + service_endpoint, + property_set: BTreeMap::new(), + } + } +} + +#[derive(Clone)] +#[wasm_bindgen] +/// DID Peer Key Purpose (used to create a new did:peer: string) +/// Verification: Keys are referenced in the authentication and assertionMethod fields +/// Encryption: Keys are referenced in the keyAgreement field +pub enum DIDPeerKeys { + Verification, + Encryption, +} + +impl fmt::Display for DIDPeerKeys { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DIDPeerKeys::Verification => write!(f, "verification"), + DIDPeerKeys::Encryption => write!(f, "encryption"), + } + } +} + +#[derive(Clone)] +#[wasm_bindgen] +/// Supported DID Peer Key Types (used to create a new did:peer: string) +pub enum DIDPeerKeyType { + Ed25519, + Secp256k1, + P256, +} + +impl fmt::Display for DIDPeerKeyType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DIDPeerKeyType::Ed25519 => write!(f, "ed25519"), + DIDPeerKeyType::Secp256k1 => write!(f, "secp256k1"), + DIDPeerKeyType::P256 => write!(f, "p256"), + } + } +} + +#[derive(Clone)] +#[wasm_bindgen(getter_with_clone)] +/// Structure to help with creating a new did:peer: string +/// purpose: ENUM (DIDPeerKeys) - Verification or Encryption +/// public_key_multibase: String - Optional: Multibase encoded public key (did:key:(.*)) +/// If None, then auto-create and return the private key +pub struct DIDPeerCreateKeys { + pub purpose: DIDPeerKeys, + pub type_: Option, + pub public_key_multibase: Option, +} + +#[wasm_bindgen] +impl DIDPeerCreateKeys { + #[wasm_bindgen(constructor)] + pub fn new( + purpose: DIDPeerKeys, + type_: Option, + public_key_multibase: Option, + ) -> Self { + DIDPeerCreateKeys { + purpose, + type_, + public_key_multibase, + } + } +} + +/// DIDPeerCreatedKeys, contains information related to any keys that were created +/// +/// key_multibase: `String`, the multibase_58 encoded key value (e.g. did:key:(.*)) +/// curve: `String`, the elliptic curve method used +/// d: `String`, private key value in Base64URL_NOPAD +/// x: `String`, public key value in Base64URL_NOPAD +/// y: `Option`, Optional: Y coordinate for EC keys in Base64URL_NOPAD +#[derive(Clone, Debug, Serialize, Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct DIDPeerCreatedKeys { + pub key_multibase: String, + pub curve: String, + pub d: String, + pub x: String, + pub y: Option, +} + +/// Converts a public key into a DID VerificationMethod +fn process_key(did: &str, id: &str, public_key: &str) -> Result { + let mut properties = BTreeMap::new(); + + properties.insert( + "publicKeyMultibase".to_string(), + Value::String(public_key.to_string()), + ); + + Ok(DIDVerificationMethod { + id: DIDURLBuf::from_string(["did:peer:", did, id].concat()).unwrap(), + type_: "Multikey".to_string(), + controller: DIDBuf::from_string(["did:peer:", did].concat()).unwrap(), + properties, + }) +} + +impl DIDMethodResolver for DIDPeer { + async fn resolve_method_representation<'a>( + &'a self, + method_specific_id: &'a str, + options: Options, + ) -> Result>, Error> { + // If did:peer is type 0, then treat it as a did:key + if let Some(id) = method_specific_id.strip_prefix('0') { + return DIDKey.resolve_method_representation(id, options).await; + } + + // Only supports method 2 for did:peer + if !method_specific_id.starts_with('2') { + return Err(Error::MethodNotSupported( + "did:peer version 2 supported only".to_string(), + )); + } + + let mut context = BTreeMap::new(); + context.insert("@base".to_string(), serde_json::json!(method_specific_id)); + + let mut verification_methods: Vec = Vec::new(); + let mut verification_relationships: VerificationRelationships = + VerificationRelationships::default(); + + //let mut key_agreements: Vec = Vec::new(); + //let mut key_authentications: Vec = Vec::new(); + //let mut key_assertion_methods: Vec = Vec::new(); + let mut services: Vec = Vec::new(); + + // Split the DID for peer on '.'s, we skip the first one + // did:peer:2.(process from here) + let parts: Vec<&str> = method_specific_id[2..].split('.').collect(); + let mut key_count: u32 = 1; + let mut service_count: u32 = 0; + + for part in parts { + let ch = part.chars().next(); + match ch { + Some(e) => { + match e { + 'A' => { + // Assertion Method + verification_methods.push(process_key( + method_specific_id, + &["#key-", &key_count.to_string()].concat(), + &part[1..], + )?); + + verification_relationships.assertion_method.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + key_count += 1; + } + 'D' => { + // Capability Delegation + verification_methods.push(process_key( + method_specific_id, + &["#key-", &key_count.to_string()].concat(), + &part[1..], + )?); + + verification_relationships.capability_delegation.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + key_count += 1; + } + 'E' => { + // Key Agreement (Encryption) + verification_methods.push(process_key( + method_specific_id, + &["#key-", &key_count.to_string()].concat(), + &part[1..], + )?); + verification_relationships.key_agreement.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + key_count += 1; + } + 'I' => { + // Capability Invocation + verification_methods.push(process_key( + method_specific_id, + &["#key-", &key_count.to_string()].concat(), + &part[1..], + )?); + + verification_relationships.capability_invocation.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + key_count += 1; + } + 'V' => { + // Authentication (Verification) + verification_methods.push(process_key( + method_specific_id, + &["#key-", &key_count.to_string()].concat(), + &part[1..], + )?); + + verification_relationships.authentication.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + verification_relationships.assertion_method.push( + verification_method::ValueOrReference::Reference( + DIDURLReferenceBuf::Relative( + RelativeDIDURLBuf::new( + ["#key-", &key_count.to_string()] + .concat() + .as_bytes() + .to_vec(), + ) + .unwrap(), + ), + ), + ); + + key_count += 1; + } + 'S' => { + // Service + let raw = match BASE64_URL_SAFE_NO_PAD.decode(part[1..].as_bytes()) { + Ok(raw) => raw, + Err(e) => { + return Err(Error::Internal(format!( + "Failed to decode base64 string: ({}) Reason: {}", + &part[1..], + e + ))); + } + }; + let service = if let Ok(service) = + serde_json::from_slice::(raw.as_slice()) + { + service + } else { + return Err(Error::Internal(format!( + "JSON parsing error on service. raw string ({})", + String::from_utf8(raw).unwrap_or("".to_string()) + ))); + }; + + let mut service: Service = service.into(); + if service_count > 0 { + service.id = UriBuf::new( + ["#service-", &service_count.to_string()].concat().into(), + ) + .unwrap(); + } + services.push(service); + service_count += 1; + } + other => { + return Err(Error::RepresentationNotSupported(format!( + "An invalid Purpose Code ({}) was found in the DID", + other + ))); + } + } + } + None => { + // We shouldn't really get here + // But it is ok if we do, we just skip it + } + } + } + + let vm_type = match options.parameters.public_key_format { + Some(name) => VerificationMethodType::from_name(&name).ok_or_else(|| { + Error::Internal(format!( + "verification method type `{name}` unsupported by did:peer" + )) + })?, + None => VerificationMethodType::Multikey, + }; + + let mut doc = + Document::new(DIDBuf::from_string(["did:peer:", method_specific_id].concat()).unwrap()); + doc.verification_method = verification_methods; + doc.verification_relationships = verification_relationships; + doc.service = services; + + let mut json_ld_context = Vec::new(); + if let Some(context) = vm_type.context_entry() { + json_ld_context.push(context) + } + + let content_type = options.accept.unwrap_or(MediaType::JsonLd); + + let represented = doc.into_representation(representation::Options::from_media_type( + content_type, + move || representation::json_ld::Options { + context: representation::json_ld::Context::array( + representation::json_ld::DIDContext::V1, + json_ld_context, + ), + }, + )); + + Ok(resolution::Output::new( + represented.to_bytes(), + document::Metadata::default(), + resolution::Metadata::from_content_type(Some(content_type.to_string())), + )) + } +} + +impl DIDMethod for DIDPeer { + const DID_METHOD_NAME: &'static str = "peer"; +} + +impl DIDPeer { + /// Creates a new did:peer DID + /// + /// This will preserve the order of the keys and services in creating the did:peer string + /// + /// # Examples + /// ```ignore + /// + /// // Create a did:peer with pre-existing encryption key (Multibase base58-btc e.g: z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK) + /// let keys = vec![DIDPeerCreateKeys { + /// type_: Some(DIDPeerKeyType::Ed25519), + /// purpose: DIDPeerKeys::Encryption, + /// public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()), + /// }]; + /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer"); + /// + /// // Create a random did:peer with services + /// let keys = vec![DIDPeerCreateKeys { + /// type_: Some(DIDPeerKeyType::Secp256k1), + /// purpose: DIDPeerKeys::Encryption, + /// public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()), + /// }]; + /// let services: Vec = vec![DIDPeerService { + /// _type: "dm".into(), + /// id: None, + /// service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong { + /// uri: "http://example.com/didcomm".into(), + /// accept: vec!["didcomm/v2".into()], + /// routing_keys: vec!["did:example:123456789abcdefghi#key-1".into()], + /// }), + /// }]; + /// + /// let did_peer = + /// DIDPeer::create_peer_did(&keys, Some(&services)).expect("Failed to create did:peer"); + /// + /// // How to create a key prior to creating a did:peer + /// let did = + /// DIDKey::generate(&JWK::generate_ed25519().unwrap()).expect("Failed to create did:key"); + /// + /// let keys = vec![DIDPeerCreateKeys { + /// type_: Some(DIDPeerKeyType::Ed25519), + /// purpose: DIDPeerKeys::Verification, + /// public_key_multibase: Some(did[8..].to_string()), + /// }]; + /// + /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer"); + /// ``` + pub fn create_peer_did( + keys: &Vec, + services: Option<&Vec>, + ) -> Result<(String, Vec), DIDPeerError> { + let mut result = String::from("did:peer:2"); + + let mut private_keys: Vec = vec![]; + for key in keys { + // Create new keys if not provided + let public_key = if let Some(key) = key.public_key_multibase.as_ref() { + key.clone() + } else { + let jwk = match &key.type_ { + Some(type_) => match type_ { + DIDPeerKeyType::Ed25519 => match JWK::generate_ed25519() { + Ok(k) => k, + Err(e) => { + return Err(DIDPeerError::InternalError(format!( + "Failed to generate ed25519 key. Reason: {}", + e + ))) + } + }, + DIDPeerKeyType::Secp256k1 => JWK::generate_secp256k1(), + DIDPeerKeyType::P256 => JWK::generate_p256(), + }, + None => return Err(DIDPeerError::UnsupportedKeyType), + }; + let did = if let Ok(output) = ssi::dids::DIDKey::generate(&jwk) { + output.to_string() + } else { + return Err(DIDPeerError::InternalError( + "Couldn't create did:key".to_string(), + )); + }; + + match jwk.params { + Params::OKP(map) => { + let d = if let Some(d) = &map.private_key { + d + } else { + return Err(DIDPeerError::KeyParsingError( + "Missing private key".to_string(), + )); + }; + private_keys.push(DIDPeerCreatedKeys { + key_multibase: did[8..].to_string(), + curve: map.curve.clone(), + d: String::from(d), + x: String::from(map.public_key.clone()), + y: None, + }) + } + Params::EC(map) => { + let curve = if let Some(curve) = &map.curve { + curve + } else { + return Err(DIDPeerError::KeyParsingError("Missing curve".to_string())); + }; + let d = if let Some(d) = &map.ecc_private_key { + d + } else { + return Err(DIDPeerError::KeyParsingError( + "Missing private key".to_string(), + )); + }; + + let x = if let Some(x) = &map.x_coordinate { + x + } else { + return Err(DIDPeerError::KeyParsingError( + "Missing public key (x)".to_string(), + )); + }; + let y = if let Some(y) = &map.y_coordinate { + y + } else { + return Err(DIDPeerError::KeyParsingError( + "Missing public key (y)".to_string(), + )); + }; + + private_keys.push(DIDPeerCreatedKeys { + key_multibase: did[8..].to_string(), + curve: curve.into(), + d: String::from(d), + x: String::from(x), + y: Some(String::from(y)), + }) + } + _ => return Err(DIDPeerError::UnsupportedKeyType), + } + + did[8..].to_string() + }; + + // Place based on key types + match key.purpose { + DIDPeerKeys::Verification => { + result.push_str(&format!(".V{}", public_key)); + } + DIDPeerKeys::Encryption => { + result.push_str(&format!(".E{}", public_key)); + } + } + } + + if let Some(services) = services { + for service in services { + let service = serde_json::to_string(&service).map_err(|e| { + DIDPeerError::SyntaxErrorServiceDefinition(format!( + "Error parsing service: {}", + e + )) + })?; + result.push_str(&format!(".S{}", BASE64_URL_SAFE_NO_PAD.encode(service))); + } + } + + Ok((result, private_keys)) + } + + /// Expands an existing DID Document from the did:key Multikeys to full JWT keys + /// This is useful for when you want to resolve a did:peer DID Document to a full JWT included DID Document + /// Converts base58 multi-keys to full JWTs in verificationMethod + pub async fn expand_keys(doc: &Document) -> Result { + let mut new_doc = doc.clone(); + + let mut new_vms: Vec = vec![]; + for v_method in &doc.verification_method { + new_vms.push(Self::_convert_vm(v_method).await?); + } + + new_doc.verification_method = new_vms; + Ok(new_doc) + } + + // Converts + async fn _convert_vm( + method: &DIDVerificationMethod, + ) -> Result { + let did_key = if let Some(key) = method.properties.get("publicKeyBase58") { + ["did:key:", key.as_str().unwrap()].concat() + } else if let Some(key) = method.properties.get("publicKeyMultibase") { + ["did:key:", key.as_str().unwrap()].concat() + } else { + return Err(DIDPeerError::KeyParsingError( + "Failed to convert verification_method. Reason: Missing publicKeyBase58" + .to_string(), + )); + }; + + let key_method = DIDKey; + + let output = match key_method + .resolve(DID::new::(&did_key.clone()).unwrap()) + .await + { + Ok(output) => output, + Err(e) => { + return Err(DIDPeerError::KeyParsingError(format!( + "Failed to resolve key ({}). Reason: {}", + did_key, e + ))); + } + }; + + if let Some(vm) = output.document.verification_method.first() { + Ok(vm.clone()) + } else { + Err(DIDPeerError::KeyParsingError( + "Failed to convert verification_method. Reason: Missing verification_method" + .to_string(), + )) + } + } +} + +// ********************************************************************************************************************************** +// WASM Specific structs and code +// ********************************************************************************************************************************** + +/// DIDService structure, input into the DidPeerCreate structure +/// +/// DIDService { +/// _type: `Option` (Optional: If not specified, defaults to 'DIDCommMessaging') +/// uri: `String` (Required: Service endpoint URI. E.g. https://localhost:7130/) +/// accept: `Vec` (Array of possible message types this service accepts) +/// routing_keys: `Vec` (Array of possible keys this Service endpoint can use) +/// id: `Option` (Optional: ID of the service. If not specified, defaults to #service) +/// } +#[wasm_bindgen(getter_with_clone)] +#[derive(Clone, Serialize, Deserialize)] +pub struct DIDService { + pub _type: Option, + pub uri: String, + pub accept: Vec, + pub routing_keys: Vec, + pub id: Option, +} + +#[wasm_bindgen] +impl DIDService { + #[wasm_bindgen(constructor)] + pub fn new( + uri: String, + accept: Vec, + routing_keys: Vec, + id: Option, + ) -> Self { + DIDService { + _type: None, + uri, + accept, + routing_keys, + id, + } + } +} + +impl From for DIDPeerService { + fn from(service: DIDService) -> Self { + DIDPeerService { + _type: service._type.unwrap_or("DIDCommMessaging".into()), + service_end_point: PeerServiceEndPoint::Short(PeerServiceEndPointShort { + uri: service.uri, + a: service.accept, + r: service.routing_keys, + }), + id: service.id, + } + } +} + +impl From<&DIDService> for DIDPeerService { + fn from(service: &DIDService) -> Self { + service.clone().into() + } +} + +/// DidPeerCreate structure, input from JS into [create_did_peer] call +/// Contains the required keys and optional services to create a new did:peer DID +/// +/// DIDPeerCreate { +/// keys: Vec<[DIDPeerCreateKeys]> (Required: Must contain at least one key for Encryption and another key for Verification) +/// services: Option (Optional: Array of DIDService structs to add to the DID Document) +/// } +#[derive(Clone)] +#[wasm_bindgen(getter_with_clone)] +pub struct DidPeerCreate { + pub keys: Vec, + pub services: Option>, +} + +#[wasm_bindgen] +impl DidPeerCreate { + #[wasm_bindgen(constructor)] + pub fn new(keys: Vec, services: Option>) -> Self { + DidPeerCreate { keys, services } + } +} + +#[derive(Serialize, Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct DIDPeerResult { + pub did: String, + pub keys: Vec, +} + +#[wasm_bindgen] +/// create_did_peer() wasm wrapper for [DIDPeer::create_peer_did] +/// Input: reference to [DidPeerCreate] struct +/// Returns: Error or String of the newly created did:peer DID +/// +/// Notes: +/// [DidPeerCreate] contains an array of keys and an optional array of Services +/// These arrays are processed in order (as in element 0 is processed first, then element 1, etc) +/// This means the key and service identifiers are auto-generated in the order they are provided +/// i.e. #service, #service-1, #service-2 and #key-1, #key-2, #key-3 ... +pub fn create_did_peer(input: &DidPeerCreate) -> Result { + // Convert DIDService to DIDPeerService + let mut new_services: Vec = vec![]; + if let Some(services) = input.services.as_ref() { + for service in services { + new_services.push(service.into()); + } + } + + // Create the did:peer DID + let response = DIDPeer::create_peer_did(&input.keys, Some(&new_services)); + + if let Ok((did, keys)) = response { + Ok(DIDPeerResult { did, keys }) + } else { + Err(response.unwrap_err()) + } +} + +#[wasm_bindgen] +/// resolve_did_peer() resolves a DID Peer method DID to a full DID Document represented by a JS object +/// Input: String of the DID Peer method DID (did:peer:2...) +/// Returns: Error or JSON String of the resolved DID Document +/// +/// NOTE: This is an async call, so you must await the result +pub async fn resolve_did_peer(did: &str) -> Result { + let peer = DIDPeer; + + match peer + .resolve(DID::new::(&did.to_string()).unwrap()) + .await + { + Ok(output) => match serde_json::to_string_pretty(&output.document) { + Ok(json) => Ok(json), + Err(e) => Err(DIDPeerError::JsonParsingError(format!( + "Couldn't convert DID Document to JSON. Reason: {}", + e + ))), + }, + Err(e) => Err(DIDPeerError::KeyParsingError(format!( + "Failed to resolve key ({}). Reason: {}", + did, e + ))), + } +} diff --git a/affinidi-did-resolver-methods/did-peer/www/did-peer.js b/affinidi-did-resolver-methods/did-peer/www/did-peer.js new file mode 100644 index 0000000..087b73a --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/www/did-peer.js @@ -0,0 +1,272 @@ +import init, * as wasm from "./pkg/did_peer.js"; + +init().then(() => { + console.log("Wasm module loaded"); +}); + +var keyRows = 0; +var serviceRows = 0; + +window.initialize = function initialize() { + addKeyRow(); + addServiceRow(true); +} + +window.clearBox = function clearBox(elementID) { + document.getElementById(elementID).innerHTML = ""; + toggleHighlight(elementID, false); +} + +window.toggleHighlight = function toggleHighlight(elementId, on) { + var el = document.getElementById(elementId); + + el.style['display'] = on ? 'block' : 'none'; +} + +window.addKeyRow = function addKeyRow() { + if (keyRows > 0) { + // remove previous buttons + document.getElementById("add_key_row").remove(); + if (document.getElementById("delete_key_row")) { + document.getElementById("delete_key_row").remove(); + } + } + + keyRows++; + var table = document.querySelector('#new-keys'); + var row = table.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(1); + var cell3 = row.insertCell(2); + var cell4 = row.insertCell(3); + var cell5 = row.insertCell(4); + cell1.innerHTML = '#key-' + keyRows; + cell2.innerHTML = ''; + cell3.innerHTML = ''; + cell4.innerHTML = ''; + cell5.innerHTML = '' + (keyRows != 1 ? ' ' : ''); + +} + +window.deleteKeyRow = function deleteKeyRow() { + if (keyRows > 0) { + document.getElementById("add_key_row").remove(); + document.getElementById("delete_key_row").remove(); + document.getElementById("new-keys").deleteRow(-1); + keyRows--; + + // Add buttons back + var table = document.querySelector('#new-keys'); + var row = table.rows[table.rows.length - 1]; + var cell = row.cells[row.cells.length - 1]; + cell.innerHTML = '' + (keyRows != 1 ? ' ' : ''); + } +} + +window.addServiceRow = function addServiceRow(initialize = false) { + + var table = document.querySelector('#new-services'); + + if (initialize) { + // No services, so add a button to create and return + var row = table.insertRow(-1); + row.insertCell(0).innerHTML = ''; + return; + } + if (serviceRows == 0) { + // Need to delete the first row and add a header row + table.deleteRow(-1); + var row = table.insertRow(-1); + + row.insertCell(0).innerHTML = 'Service ID'; + row.insertCell(1).innerHTML = 'Type'; + row.insertCell(2).innerHTML = 'URI'; + row.insertCell(3).innerHTML = 'Accept'; + row.insertCell(4).innerHTML = 'Routing Keys'; + } + if (serviceRows > 0) { + // remove previous buttons + document.getElementById("add_service_row").remove(); + if (document.getElementById("delete_service_row")) { + document.getElementById("delete_service_row").remove(); + } + } + + serviceRows++; + var row = table.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(1); + var cell3 = row.insertCell(2); + var cell4 = row.insertCell(3); + var cell5 = row.insertCell(4); + var cell6 = row.insertCell(5); + cell1.innerHTML = '#service' + (serviceRows > 1 ? "-" + (serviceRows - 1) : ""); + cell2.innerHTML = 'DIDCommMessaging'; + cell3.innerHTML = ''; + cell4.innerHTML = ''; + cell5.innerHTML = ''; + cell6.innerHTML = ''; +} + +window.deleteServiceRow = function deleteServiceRow() { + if (serviceRows > 0) { + document.getElementById("add_service_row").remove(); + document.getElementById("delete_service_row").remove(); + document.getElementById("new-services").deleteRow(-1); + serviceRows--; + + if (serviceRows == 0) { + // re-initialize the services table + document.getElementById("new-services").deleteRow(-1); + addServiceRow(true); + } else { + // Add buttons back to previous row + var table = document.querySelector('#new-services'); + var row = table.rows[table.rows.length - 1]; + var cell = row.cells[row.cells.length - 1]; + cell.innerHTML = ''; + } + } +} + +window.copyDid = function copyDid(id) { + // Get the text field + var copyText = document.getElementById(id); + + + // Copy the text inside the text field + navigator.clipboard.writeText(copyText.innerHTML); + +} + +window.resolveDID = function resolveDID() { + let did = document.getElementById('did_resolve').value; + try { + wasm.resolve_did_peer(did).then((didDocument) => { + if (didDocument == undefined) { + console.log("ERROR"); + } else { + let test = JSON.parse(didDocument); + document.getElementById("resolve-result").innerHTML = "

" + JSON.stringify(test, null, 2) + "
"; + toggleHighlight("resolve-result", true); + } + }).catch((e) => { + document.getElementById("resolve-result").innerHTML = e; + toggleHighlight("resolve-result", true); + }) + } catch (e) { + console.log(e); + } +} + +window.createDID = function createDID() { + // create an array of keys specified + var table = document.querySelector('#new-keys'); + var rows = table.rows; + let keys = []; + // We start at 1 as this table has a header. + for (let i = 1; i < rows.length; i++) { + let keyPurpose = 0; + let e = document.getElementById('keys-purpose-' + i); + let v = e.value; + switch (e.options[e.selectedIndex].text) { + case "Verification": + keyPurpose = wasm.DIDPeerKeys.Verification; + break; + case "Encryption": + keyPurpose = wasm.DIDPeerKeys.Encryption; + break; + default: + console.log("ERROR"); + } + + let keyType = 0; + e = document.getElementById('keys-type-' + i); + v = e.value; + switch (e.options[e.selectedIndex].text) { + case "Ed25519": + keyType = wasm.DIDPeerKeyType.Ed25519; + break; + case "Secp256K1": + keyType = wasm.DIDPeerKeyType.Secp256k1; + break; + case "P256": + keyType = wasm.DIDPeerKeyType.P256; + break; + + default: + console.log("ERROR"); + } + let multibase = document.getElementById('keys-multibase-' + i).value; + keys.push(new wasm.DIDPeerCreateKeys(keyPurpose, keyType, multibase == "" ? null : multibase)); + } + + // create a list of services specified + table = document.querySelector('#new-services'); + rows = table.rows; + let services = []; + // We start at 1 as this table has a header. + for (let i = 1; i < rows.length; i++) { + var t = document.getElementById('service-uri-' + i); + let v = t.value; + let uri = document.getElementById('service-uri-' + i).value; + let accept = String(document.getElementById('service-accept-' + i).value).replace(/;+$/, "").split(";"); + let rk = String(document.getElementById('service-routing-' + i).value); + let routingKeys = rk == "" ? [] : rk.split(";"); + services.push(new wasm.DIDService(uri, accept, routingKeys)); + } + + let result = wasm.create_did_peer(new wasm.DidPeerCreate(keys, services)); + document.getElementById("create-result-did").innerHTML = "Peer DID Identifier

" + result.did + '

'; + toggleHighlight("create-result-did", true); + + document.getElementById("create-result-keys").innerHTML = "Private Key Materials
"; + let e = document.getElementById("create-result-keys"); + e.innerHTML = '
'; + table = document.getElementById("key-table"); + for (let i = 0; i < result.keys.length; i++) { + if (i > 0) { + table.insertRow().insertCell(0).innerHTML = "
"; + + } + + let row = table.insertRow(); + let cell = row.insertCell(0); + cell.innerHTML = "Key ID"; + cell = row.insertCell(1); + cell.innerHTML = "#key-" + (i + 1) + '
'; + + row = table.insertRow(); + cell = row.insertCell(0); + cell.innerHTML = "Multibase Key"; + cell = row.insertCell(1); + cell.innerHTML = result.keys[i].key_multibase; + + row = table.insertRow(); + cell = row.insertCell(0); + cell.innerHTML = "Curve"; + cell = row.insertCell(1); + cell.innerHTML = result.keys[i].curve; + + row = table.insertRow(); + cell = row.insertCell(0); + cell.innerHTML = "Private Key (d)"; + cell = row.insertCell(1); + cell.innerHTML = result.keys[i].d; + + row = table.insertRow(); + cell = row.insertCell(0); + cell.innerHTML = "Public Key (x)"; + cell = row.insertCell(1); + cell.innerHTML = result.keys[i].x; + + if (result.keys[i].y != null) { + row = table.insertRow(); + cell = row.insertCell(0); + cell.innerHTML = "Public Key (y)"; + cell = row.insertCell(1); + cell.innerHTML = result.keys[i].y; + } + } + toggleHighlight("create-result-keys", true); +} \ No newline at end of file diff --git a/affinidi-did-resolver-methods/did-peer/www/index.html b/affinidi-did-resolver-methods/did-peer/www/index.html new file mode 100644 index 0000000..b42206b --- /dev/null +++ b/affinidi-did-resolver-methods/did-peer/www/index.html @@ -0,0 +1,207 @@ + + + + + + Affinidi Peer DID example + + + + + + +
+

Resolve Peer DID

+

NOTE:Only DID:peer:2. (method type 2) is supported.

+
+ + + + + + + + + + +
+
+ +
+
+

Resolver Result ::

+
+
+
+
+
+

Create a DID:peer

+ + To create a new DID:peer, you need to provide at least one key. You can add more keys and services if + needed.
+ NOTE: Only DID:peer:2. (method type 2) is supported. + +

Keys

+ + DID:peer supports two types of keys: Verification and Encryption. You can add multiple keys of each type. +

+ + + + + + + + + + + + + + + + + +
+
  • Verification
  • +
    - Appears in the DID Document authentication and assertionMethod fields
    +
  • Recommend Ed25519 key type for signing
  • +
    +
  • Encryption
  • +
    - Appears in the DID Document keyAgreement field
    +
  • Recommend Secp256K1 key type for encryption, supports recryption capabilities
  • +
    +
    + You can specify an existing Multibase Public Key if already created (i.e. for example did:key:(z6Mk...)). +
    If left blank, keys will be autogenerated and private keys shown below.
    +

    + Important: For DIDComm you must create a new DID:peer with at least one Verification and + one Encryption key. +

    + NOTE: The order of these keys reflects the key identifier names used!
    + NOTE: For simplicity auto-generated keys are Ed25519 based. EdDSA + Curve25519 +

    + + + + + + + + +
    Key IDPurposeTypeMultibase Public Key
    +

    Services

    + + DID:peer allows you to specify service statements that can be used for example to describe how DIDComm messages + can be sent to a mediator service. You can specify multiple services. +

    + + + + + + + + + + + + + + + + + +
    +
  • Type
  • +
    - Defaults to DIDCommMessaging
    +
  • URI
  • +
    - Service endpoint address E.g. (https://127.0.0.1:7037/)
    +
  • Accept
  • +
    - Array of accepted message types for this service. Defaults to didcomm/v2
    +
  • Routing Keys
  • +
    - Array of DID public-keys that can be used for this defined service. Format must be + did:method:identifier#key-id +
    +
    + NOTE: The order of these services reflects the service identifier names used!
    + NOTE: All attributes are required in describing the service!
    + NOTE: Separate multiple items in Arrays with ';'
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    Examples

    + Alice wants to send a DIDComm message to Bob. Bob utilizes a DIDComm mediator to handle his incoming messages, + this + is described in Bob's DID Document via a defined service. + +

    Alice

    + Alice represents herself with a Decentralized Identifier (DID) that contains the following: +
      1. Verification key that is used for signing of messages for non-repudiation
    +
      2. Encryption key that is used for encryption of messages that are sent to Alice
    +
      3. Basic service definition that states she accepts DIDComm messaging directly
    +

    Example Alice DID 

    + + did:peer:2.Vz6MknrybrXop8wjecgH5gNZ4bajiztB1EuJ1aPJW1PsLaFcP.EzDnaeYzK9dTHipxp7QxvUADomBdLsSSvPsHBMc93g4BvSviHZ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX19 + +

    Bob

    + Bob represents himself with a Decentralized Identifier (DID) that contains the following: +
      1. Verification key that is used for signing of messages for non-repudiation
    +
      2. Encryption key that is used for encryption of messages that are sent to Bob
    +
      3. Service definition that states he uses a DIDComm mediator to receive messages. He shares the public-key + to + use when communicating to the mediator
    +

    Example Bob DID 

    + + did:peer:2.Vz6MkfRViUo3fADYPnpQeyJLCgtGsMfwqSUcEq7Jx6VrbA2Yg.EzDnaezTcKcJKooedjzf1tzFZs1eqVJwipRWDjDHJ7svXDpm6C.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiLCJkaWRjb21tL2FpcDI7ZW52PXJmYzU4NyJdLCJyb3V0aW5nX2tleXMiOlsiZGlkOnBlZXI6Mi5WejZNa3Y1cGNtRXN6a3A0dExmQXZIZk14TUU3R1R3VnNwdUVURG9uOUMyTFh5UzQ5LkV6RG5hZXBjdmNRaWZFdUdrdGpGYXpaMUZNVHpFTGY2aFdXY1dvRmFRZFRKclI4OUx6LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQnpPaTh2Ykc5allXeG9iM04wT2pjd016Y3ZJaXdpWVdOalpYQjBJanBiSW1ScFpHTnZiVzB2ZGpJaVhTd2ljbTkxZEdsdVoxOXJaWGx6SWpwYlhYMTkja2V5LTIiXX19 + +

    Mediator

    + Affinidi offers a mediator node that can be used to route messages between two parties. Bob uses this mediator + to + receive messages.
    + This mediator is referenced in Bob's DID Document via a defined service for DIDComm messaging. The Mediator's + DID + contains: +
      1. Verification key that is used for signing of messages for non-repudiation
    +
      2. Encryption key that is used for encryption of messages that are sent to Bob
    +
      3. Service definition that shares the address for Affinidi Secure Messaging
    +

    Example Mediator DID 

    + + did:peer:2.Vz6Mkv5pcmEszkp4tLfAvHfMxME7GTwVspuETDon9C2LXyS49.EzDnaepcvcQifEuGktjFazZ1FMTzELf6hWWcWoFaQdTJrR89Lz.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX19 + + + + \ No newline at end of file diff --git a/affinidi-did-resolver-methods/src/lib.rs b/affinidi-did-resolver-methods/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/affinidi-did-resolver-methods/src/lib.rs @@ -0,0 +1 @@ + diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..5145c55 --- /dev/null +++ b/deny.toml @@ -0,0 +1,64 @@ +[advisories] +version = 2 +ignore = [ + { id = "RUSTSEC-2022-0092", reason = "askalono always provides valid utf-8 files from a cache, this is not relevant" }, +] + +[bans] +multiple-versions = "allow" + +[licenses] +# We want really high confidence when inferring licenses from text +confidence-threshold = 0.93 +allow = [ + "Apache-1.1", + "Apache-2.0", + "BSL-1.0", + "BSD-1-Clause", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "WTFPL", + "Fair", + "Intel", + "ISC", + "MIT-0", + "MIT", + "MIT-Modern-Variant", + "MulanPSL-2.0", + "Multics", + "Naumen", + "PHP-3.01", + "PostgreSQL", + "Python-2.0", + "OFL-1.1", + "MirOS", + "Unlicense", + "Unicode-DFS-2016", + "UPL-1.0", + "NCSA", + "Zlib", + "AFL-2.1", + "OpenSSL", + "ISC", + "MPL-2.0", + "W3C-20150513", +] + +exceptions = [ + { allow = ["OpenSSL"], crate = "ring" }, +] + +# Sigh +[[licenses.clarify]] +crate = "ring" +# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses +# https://spdx.org/licenses/OpenSSL.html +# ISC - Both BoringSSL and ring use this for their new files +# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT +# license, for third_party/fiat, which, unlike other third_party directories, is +# compiled into non-test libraries, is included below." +# OpenSSL - Obviously +expression = "ISC AND MIT AND OpenSSL" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]