From 2f81934f78d315f788f7be7fd4560f61d01b982c Mon Sep 17 00:00:00 2001 From: James Sadler Date: Tue, 6 Oct 2026 22:37:04 +1100 Subject: [PATCH 1/3] feat(stack-encrypt): serve vendor keysets from a static registry A deployment on AWS KMS, Azure Key Vault, Cloud KMS or Vault Transit has no keyset service, so ADR 0007 makes each keyset a configuration entry: an id, an optional name, and a provider bound to one backend key with the persisted KeyId of its index key. Until now nothing in this crate turned such entries into a KeysetRegistry, so every vendor deployment had to write one. StaticKeysetRegistry is that registry. Its first keyset is the default. A keyset resolves by id, and by name if it has one. Any other reference is Ok(None), so the cipher reports UnknownKeyset. It never fails to answer, and it does not read format-1 leaves, which only ZeroKMS wrote. Duplicate ids or names are refused when the table is built. A registry hands out its provider by value, and FixedIndexKeySource is not Clone, so SharedProvider shares each provider through an Arc. The module has no vendor dependency: the caller turns on the vendor feature of vitaminc-kms. FixedIndexKeySource is re-exported beside the other provider types, so a caller names it from the same copy of vitaminc-kms. --- packages/stack-encrypt/src/registry.rs | 9 +- .../src/registry/static_keysets.rs | 378 ++++++++++++++++++ .../stack-encrypt/tests/registry_static.rs | 193 +++++++++ .../tests/ui/execution_callback.stderr | 100 +++-- 4 files changed, 642 insertions(+), 38 deletions(-) create mode 100644 packages/stack-encrypt/src/registry/static_keysets.rs create mode 100644 packages/stack-encrypt/tests/registry_static.rs diff --git a/packages/stack-encrypt/src/registry.rs b/packages/stack-encrypt/src/registry.rs index 3151a1c06..4bb176f6e 100644 --- a/packages/stack-encrypt/src/registry.rs +++ b/packages/stack-encrypt/src/registry.rs @@ -3,7 +3,8 @@ //! //! A [`KeysetRegistry`] is the deployment's map. ZeroKMS's implementation //! asks the service and gets a keyset back; a vendor deployment's reads a -//! static table of backend keys it was configured with. Either way the +//! static table of backend keys it was configured with +//! ([`StaticKeysetRegistry`]). Either way the //! answer is one [`vitaminc_kms::provider::KeyProvider`] bound to one //! backend key, which is the only shape `vitaminc-kms` offers — selection //! is this crate's business, not that crate's. See cipherstash/vitaminc @@ -25,7 +26,8 @@ use vitaminc_kms::provider::MaybeSend; /// differently-sourced `vitaminc-kms` is a different `KeyProvider` trait at /// compile time, and an impl written against it would not satisfy this one. pub use vitaminc_kms::provider::{ - Binding, BindingSupport, IndexKeyProvider, KeyProvider, MaybeSend as ProviderMaybeSend, + Binding, BindingSupport, FixedIndexKeySource, IndexKeyProvider, KeyProvider, + MaybeSend as ProviderMaybeSend, }; pub use vitaminc_kms::{ GeneratedDataKey, IndexKeyMaterial, KeyId, KeyIsolation, KeyReconstruction, @@ -35,9 +37,12 @@ pub use vitaminc_protected_kms::Protected as ProviderProtected; #[cfg(any(test, feature = "test-support"))] pub mod fake; +mod static_keysets; #[cfg(feature = "zerokms")] mod zerokms; +pub use static_keysets::{SharedProvider, StaticKeyset, StaticKeysetRegistry, StaticRegistryError}; + /// A keyset's identity: globally unique, carried in every sealed leaf, and /// never re-checked once resolved. /// diff --git a/packages/stack-encrypt/src/registry/static_keysets.rs b/packages/stack-encrypt/src/registry/static_keysets.rs new file mode 100644 index 000000000..0d9c3b072 --- /dev/null +++ b/packages/stack-encrypt/src/registry/static_keysets.rs @@ -0,0 +1,378 @@ +//! A [`KeysetRegistry`] over a fixed table of keysets, for a deployment +//! whose backend has no keyset service of its own. +//! +//! On ZeroKMS a keyset is a service-side object. On AWS KMS, Azure Key +//! Vault, Cloud KMS or Vault Transit there is no such object, so a keyset is +//! a deployment configuration entry: an id, an optional name, and a +//! provider bound to one backend key together with the persisted `KeyId` of +//! that key's index key. `vitaminc-kms`'s +//! [`FixedIndexKeySource`](super::FixedIndexKeySource) is exactly that +//! provider. See ADR 0007. +//! +//! This module has no vendor dependency. It is generic over the provider, +//! so the vendor SDK is the caller's dependency, through `vitaminc-kms` and +//! the vendor feature the caller turns on. + +use std::sync::Arc; + +use super::{ + Binding, BindingSupport, GeneratedDataKey, IndexKeyMaterial, IndexKeyProvider, KeyId, + KeyIsolation, KeyProvider, KeyReconstruction, KeysetId, KeysetRef, KeysetRegistry, + ProviderProtected, Resolved, +}; + +/// One entry of a [`StaticKeysetRegistry`]: a keyset id, an optional name, +/// and the provider that serves the keyset. +pub struct StaticKeyset

{ + id: KeysetId, + name: Option, + provider: P, +} + +impl

StaticKeyset

{ + /// A keyset with this id and no name. It resolves by id only. + /// + /// The id is what every leaf sealed under the keyset carries. Choose it + /// once, and keep it with the provider's backend key and index `KeyId`: + /// a leaf whose keyset id the registry does not know fails with + /// [`Error::UnknownKeyset`](crate::Error::UnknownKeyset). + pub fn new(id: impl Into, provider: P) -> Self { + Self { + id: id.into(), + name: None, + provider, + } + } + + /// Give the keyset a name, so that it also resolves by that name. + pub fn named(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } +} + +/// Why a [`StaticKeysetRegistry`] refused a keyset. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum StaticRegistryError { + /// Two keysets have the same id. A leaf names its keyset by id, so the + /// id must select one keyset only. + #[error("two keysets have the id {0}")] + DuplicateId(KeysetId), + /// Two keysets have the same name. + #[error("two keysets have the name {0:?}")] + DuplicateName(String), +} + +/// A provider that a [`StaticKeysetRegistry`] holds once and hands out on +/// every resolution. +/// +/// A registry gives a provider by value each time it resolves a keyset, and +/// a provider such as `FixedIndexKeySource` is not `Clone`. This wrapper +/// shares one provider through an `Arc`, so each resolution is the same +/// provider, with the same client and the same connection pool. It +/// forwards every call, and the three capability constants, to that +/// provider. +pub struct SharedProvider

(Arc

); + +impl

Clone for SharedProvider

{ + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + +impl

SharedProvider

{ + /// The provider this shares. + pub fn inner(&self) -> &P { + &self.0 + } +} + +impl KeyProvider for SharedProvider

+where + P: KeyProvider + Send + Sync, +{ + type Error = P::Error; + + const RECONSTRUCTION: KeyReconstruction = P::RECONSTRUCTION; + const ISOLATION: KeyIsolation = P::ISOLATION; + const BINDING: BindingSupport = P::BINDING; + + async fn generate_keys( + &self, + bindings: &[Binding<'_>], + ) -> Result>, Self::Error> { + self.0.generate_keys(bindings).await + } + + async fn retrieve_keys( + &self, + keys: &[(KeyId, Binding<'_>)], + ) -> Result>, Self::Error> { + self.0.retrieve_keys(keys).await + } +} + +impl IndexKeyProvider for SharedProvider

+where + P: IndexKeyProvider + Send + Sync, +{ + type Error = P::Error; + + async fn load_index_key(&self) -> Result, Self::Error> { + self.0.load_index_key().await + } +} + +struct Entry

{ + id: KeysetId, + name: Option, + provider: SharedProvider

, +} + +impl

From> for Entry

{ + fn from(keyset: StaticKeyset

) -> Self { + Self { + id: keyset.id, + name: keyset.name, + provider: SharedProvider(Arc::new(keyset.provider)), + } + } +} + +/// A [`KeysetRegistry`] over a fixed table of keysets, all served by one +/// provider type. +/// +/// The first keyset is the default: [`KeysetRef::Default`] resolves to it, +/// and [`StackCipher`](crate::StackCipher) loads it when the cipher is +/// built. A keyset resolves by its id, and by its name if it has one. Any +/// other reference is `Ok(None)`, which the cipher reports as +/// [`Error::UnknownKeyset`](crate::Error::UnknownKeyset). The registry +/// never fails to answer, so its error type is +/// [`Infallible`](std::convert::Infallible). +/// +/// It does not read format-1 leaves +/// ([`READS_V1_LEAVES`](KeysetRegistry::READS_V1_LEAVES) is `false`): only +/// ZeroKMS wrote them. +/// +/// ``` +/// # async fn example() -> Result<(), Box> { +/// use stack_encrypt::registry::{ +/// Binding, FixedIndexKeySource, KeyProvider, ProviderProtected, StaticKeyset, +/// StaticKeysetRegistry, +/// }; +/// use stack_encrypt::{nonempty, StackCipherBuilder}; +/// use uuid::Uuid; +/// +/// // A data key source bound to one backend key. In a deployment this is, +/// // for example, `vitaminc_kms::AwsDataKeySource<32>`. +/// let source = vitaminc_kms::provider::FakeKeyProvider::<32>::with_index_key( +/// ProviderProtected::new([7; 32]), +/// ); +/// +/// // Provision the index key once: generate one data key and store its +/// // `KeyId` with the deployment's configuration. Every later run reads +/// // that `KeyId` back. A new one makes all earlier index terms unfindable. +/// let index_key_id = source.generate_keys(&[Binding::EMPTY]).await?.remove(0).key_id; +/// +/// let registry = StaticKeysetRegistry::new( +/// StaticKeyset::new(Uuid::from_u128(1), FixedIndexKeySource::new(source, index_key_id)) +/// .named("main"), +/// ); +/// let cipher = StackCipherBuilder::new().registry(registry).init().await?; +/// let sealed = cipher +/// .keyset("main") +/// .await? +/// .encrypt("hello".to_string(), nonempty!("greeting")) +/// .await?; +/// let opened: String = cipher.decrypt(sealed, nonempty!("greeting")).await?; +/// assert_eq!(opened, "hello"); +/// # Ok(()) +/// # } +/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(example()).unwrap(); +/// ``` +pub struct StaticKeysetRegistry

{ + /// The default keyset first, then the others in the order they were + /// added. A deployment has a few keysets, so a scan is enough. + keysets: Vec>, +} + +impl

StaticKeysetRegistry

{ + /// A registry whose default keyset is `default`. + pub fn new(default: StaticKeyset

) -> Self { + Self { + keysets: vec![default.into()], + } + } + + /// Add a keyset that a caller selects by id or by name. + /// + /// A keyset whose id or name is already in the registry is refused: an + /// id or a name must select one keyset only. + pub fn with_keyset(mut self, keyset: StaticKeyset

) -> Result { + if self.keysets.iter().any(|entry| entry.id == keyset.id) { + return Err(StaticRegistryError::DuplicateId(keyset.id)); + } + if let Some(name) = &keyset.name { + if self.find_name(name).is_some() { + return Err(StaticRegistryError::DuplicateName(name.clone())); + } + } + self.keysets.push(keyset.into()); + Ok(self) + } + + /// The id of the default keyset. + pub fn default_id(&self) -> KeysetId { + self.default_entry().id + } + + fn default_entry(&self) -> &Entry

{ + // `new` puts the default first, and nothing removes an entry. + &self.keysets[0] + } + + fn find_name(&self, name: &str) -> Option<&Entry

> { + self.keysets + .iter() + .find(|entry| entry.name.as_deref() == Some(name)) + } + + fn find(&self, keyset: &KeysetRef) -> Option<&Entry

> { + match keyset { + KeysetRef::Default => Some(self.default_entry()), + KeysetRef::Id(id) => self.keysets.iter().find(|entry| entry.id == *id), + KeysetRef::Name(name) => self.find_name(name), + } + } +} + +impl

KeysetRegistry for StaticKeysetRegistry

+where + P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, +{ + type Provider = SharedProvider

; + type Error = std::convert::Infallible; + + // Only ZeroKMS wrote format-1 leaves, and their key id is ZeroKMS's + // `iv ‖ tag`, which no other backend parses. + const READS_V1_LEAVES: bool = false; + + async fn resolve( + &self, + keyset: &KeysetRef, + ) -> Result>, Self::Error> { + Ok(self.find(keyset).map(|entry| Resolved { + id: entry.id, + // The name asked for, not a property of the keyset: a selection + // by id or by default looked nothing up and carries none. + name: keyset.name().map(str::to_owned), + provider: entry.provider.clone(), + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use uuid::Uuid; + use vitaminc_kms::provider::FakeKeyProvider; + use vitaminc_protected_kms::Protected; + + fn provider(seed: u8) -> FakeKeyProvider<32> { + FakeKeyProvider::with_index_key(Protected::new([seed; 32])) + } + + fn id(n: u128) -> KeysetId { + KeysetId::new(Uuid::from_u128(n)) + } + + async fn resolved( + registry: &StaticKeysetRegistry>, + keyset: impl Into, + ) -> Option<(KeysetId, Option)> { + let Ok(found) = registry.resolve(&keyset.into()).await; + found.map(|resolved| (resolved.id, resolved.name)) + } + + fn registry() -> StaticKeysetRegistry> { + StaticKeysetRegistry::new(StaticKeyset::new(id(1), provider(1)).named("main")) + .with_keyset(StaticKeyset::new(id(2), provider(2)).named("acme")) + .unwrap() + .with_keyset(StaticKeyset::new(id(3), provider(3))) + .unwrap() + } + + #[tokio::test] + async fn the_default_is_the_first_keyset() { + let registry = registry(); + assert_eq!(registry.default_id(), id(1)); + assert_eq!( + resolved(®istry, KeysetRef::Default).await, + Some((id(1), None)), + "a selection by default looked no name up" + ); + } + + #[tokio::test] + async fn a_keyset_resolves_by_its_id() { + let registry = registry(); + for n in 1..=3 { + assert_eq!(resolved(®istry, id(n)).await, Some((id(n), None))); + } + } + + #[tokio::test] + async fn a_named_keyset_resolves_by_its_name_and_carries_it() { + let registry = registry(); + assert_eq!( + resolved(®istry, "acme").await, + Some((id(2), Some("acme".to_owned()))) + ); + assert_eq!( + resolved(®istry, "main").await, + Some((id(1), Some("main".to_owned()))), + "the default keyset resolves by its name too" + ); + } + + #[tokio::test] + async fn anything_else_is_the_registrys_own_no() { + let registry = registry(); + assert_eq!(resolved(®istry, id(4)).await, None); + assert_eq!(resolved(®istry, "other").await, None); + assert_eq!( + resolved(®istry, "").await, + None, + "a keyset with no name does not answer to the empty name" + ); + } + + #[test] + fn an_id_or_a_name_selects_one_keyset_only() { + assert_eq!( + registry() + .with_keyset(StaticKeyset::new(id(3), provider(9))) + .err(), + Some(StaticRegistryError::DuplicateId(id(3))) + ); + assert_eq!( + registry() + .with_keyset(StaticKeyset::new(id(9), provider(9)).named("main")) + .err(), + Some(StaticRegistryError::DuplicateName("main".to_owned())) + ); + assert!( + registry() + .with_keyset(StaticKeyset::new(id(9), provider(9))) + .is_ok(), + "two keysets with no name do not collide" + ); + } + + #[test] + fn vendor_keysets_do_not_read_format_1_leaves() { + const { + assert!(!> as KeysetRegistry>::READS_V1_LEAVES) + }; + } +} diff --git a/packages/stack-encrypt/tests/registry_static.rs b/packages/stack-encrypt/tests/registry_static.rs new file mode 100644 index 000000000..26d812593 --- /dev/null +++ b/packages/stack-encrypt/tests/registry_static.rs @@ -0,0 +1,193 @@ +//! `StaticKeysetRegistry`: a vendor deployment's keysets, end to end through +//! `StackCipher`. +//! +//! Each keyset is served the way a vendor keyset is: a data key source bound +//! to one backend key, wrapped in `FixedIndexKeySource` with the `KeyId` of +//! an index key provisioned once. `FakeKeyProvider` stands in for the vendor +//! source. It binds each key to its descriptor and refuses a key id it did +//! not mint, so the index key here really is the provisioned key. + +use stack_encrypt::registry::{ + Binding, FixedIndexKeySource, IndexKeyProvider, KeyId, KeyProvider, ProviderProtected, + StaticKeyset, StaticKeysetRegistry, +}; +use stack_encrypt::{ + nonempty, CipherText, Error, KeysetId, KeysetRegistry, SealedValue, StackCipher, + StackCipherBuilder, StackCipherText, +}; +use uuid::Uuid; +use vitaminc_kms::provider::FakeKeyProvider; +use vitaminc_protected_kms::Controlled; + +type Source = FixedIndexKeySource>; + +const MAIN: Uuid = Uuid::from_u128(0x0a); +const ACME: Uuid = Uuid::from_u128(0x0b); + +/// A vendor keyset as a deployment configures it: the source, and the +/// `KeyId` of the index key it provisioned once. +async fn provisioned(seed: u8) -> (Source, KeyId) { + // The fake's own index key is never used: `FixedIndexKeySource` asks the + // source for the provisioned key instead. + let source = FakeKeyProvider::with_index_key(ProviderProtected::new([seed; 32])); + let index_key_id = source + .generate_keys(&[Binding::EMPTY]) + .await + .expect("mint the index key") + .remove(0) + .key_id; + ( + FixedIndexKeySource::new(source, index_key_id.clone()), + index_key_id, + ) +} + +async fn cipher() -> StackCipher> { + let (main, _) = provisioned(1).await; + let (acme, _) = provisioned(2).await; + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, main).named("main")) + .with_keyset(StaticKeyset::new(ACME, acme).named("acme")) + .expect("distinct ids and names"); + StackCipherBuilder::new() + .registry(registry) + .init() + .await + .expect("build cipher") +} + +/// The index key a keyset serves is the key its source minted for the +/// provisioned `KeyId`, retrieved under the empty binding, and the shared +/// provider the registry hands out serves that same key. +#[tokio::test] +async fn the_index_key_is_the_provisioned_key() { + let (source, index_key_id) = provisioned(1).await; + let minted = source + .retrieve_keys(&[(index_key_id, Binding::EMPTY)]) + .await + .expect("the source minted it") + .remove(0); + + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, source)); + let resolved = registry + .resolve(&MAIN.into()) + .await + .expect("infallible") + .expect("known"); + let served = resolved.provider.load_index_key().await.expect("index key"); + assert_eq!(served.0.risky_unwrap(), minted.risky_unwrap()); + let _: &Source = resolved.provider.inner(); +} + +/// A value sealed under a keyset selected by name opens again, and the leaf +/// names the keyset by its id. +#[tokio::test] +async fn a_value_round_trips_under_each_keyset() { + let cipher = cipher().await; + + for (selector, id) in [("main", MAIN), ("acme", ACME)] { + let keyset = cipher.keyset(selector).await.expect("known keyset"); + assert_eq!(keyset.keyset_id(), KeysetId::new(id)); + let sealed: StackCipherText = keyset + .encrypt("hello".to_string(), nonempty!("greeting")) + .await + .expect("seal"); + let CipherText::Single(leaf) = &sealed else { + panic!("a scalar seals to one leaf"); + }; + assert_eq!(leaf.keyset_id(), KeysetId::new(id)); + + let opened: String = cipher + .decrypt(sealed, nonempty!("greeting")) + .await + .expect("open"); + assert_eq!(opened, "hello"); + } + + let by_default: StackCipherText = cipher + .default_keyset() + .encrypt(7u32, nonempty!("count")) + .await + .expect("seal"); + let opened: u32 = cipher + .keyset(KeysetId::new(MAIN)) + .await + .expect("known by id") + .decrypt(by_default, nonempty!("count")) + .await + .expect("the default keyset is MAIN"); + assert_eq!(opened, 7); +} + +/// Two keysets have two index keys. One shared index key would make every +/// keyset's terms comparable with every other's. +#[tokio::test] +async fn each_keyset_serves_its_own_index_key() { + let cipher = cipher().await; + let registry = cipher.registry(); + let mut keys = Vec::new(); + for selector in ["main", "acme"] { + let resolved = registry + .resolve(&selector.into()) + .await + .expect("infallible") + .expect("known"); + let key = resolved.provider.load_index_key().await.expect("index key"); + keys.push(key.0.risky_unwrap()); + } + assert_ne!(keys[0], keys[1]); +} + +/// A keyset the table does not hold is the registry's own "no", which the +/// cipher reports as `UnknownKeyset`. +#[tokio::test] +async fn an_unknown_keyset_is_refused() { + let cipher = cipher().await; + for selector in [ + stack_encrypt::KeysetRef::from("other"), + stack_encrypt::KeysetRef::from(Uuid::from_u128(0x0c)), + ] { + let refused = cipher.keyset(selector).await.map(|_| ()); + assert!( + matches!(refused, Err(Error::UnknownKeyset { .. })), + "{refused:?}" + ); + } +} + +/// `"alice@example.com"`, sealed by stack-encrypt 0.2 in the format-1 layout +/// under keyset `5e7f0000-0000-4000-8000-000000000001` (the fixture in +/// `tests/format_v1.rs`). +const EMAIL_V1: &str = "015e7f000000004000800000000000000101010101010101010101010101010101100076312d666978747572652d7461672d310153767116f8f0c7450ebd6cf71a4afd93d9d7d037b51a48c3ab9d68480cf3ba4dc5d6a986ccbafb60d629baf402"; +const V1_KEYSET: Uuid = Uuid::from_u128(0x5e7f_0000_0000_4000_8000_0000_0000_0001); + +/// Only ZeroKMS wrote format-1 leaves, so a vendor registry refuses one with +/// the error that says why, even when it holds a keyset with that id. +#[tokio::test] +async fn a_format_1_leaf_is_refused() { + let (main, _) = provisioned(1).await; + let (v1, _) = provisioned(3).await; + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, main)) + .with_keyset(StaticKeyset::new(V1_KEYSET, v1)) + .expect("distinct ids"); + let cipher = StackCipherBuilder::new() + .registry(registry) + .init() + .await + .expect("build cipher"); + + let bytes: Vec = (0..EMAIL_V1.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&EMAIL_V1[i..i + 2], 16).expect("hex")) + .collect(); + let leaf = SealedValue::from_bytes(&bytes).expect("a format-1 leaf parses"); + let opened: Result = cipher + .decrypt(CipherText::Single(leaf), "users/email") + .await; + assert!( + matches!( + opened, + Err(Error::V1LeafNeedsZeroKms { keyset_id }) if keyset_id == KeysetId::new(V1_KEYSET) + ), + "{opened:?}" + ); +} diff --git a/packages/stack-encrypt/tests/ui/execution_callback.stderr b/packages/stack-encrypt/tests/ui/execution_callback.stderr index 14048d4d6..1e8996202 100644 --- a/packages/stack-encrypt/tests/ui/execution_callback.stderr +++ b/packages/stack-encrypt/tests/ui/execution_callback.stderr @@ -5,24 +5,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `Encryption` --> src/target/operations.rs | @@ -39,24 +46,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | |__________^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `Encryption` --> src/target/operations.rs | @@ -70,24 +84,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `KeysetCipher` --> src/keyset.rs | @@ -101,24 +122,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `stack_encrypt::Pending` --> src/target/pending.rs | From 92cbabb4ca6bcc6613dda1f59213c88e0f422c6d Mon Sep 17 00:00:00 2001 From: James Sadler Date: Tue, 6 Oct 2026 22:40:06 +1100 Subject: [PATCH 2/3] feat(examples): run stack-encrypt on AWS KMS through the static registry The demo shows that stack-encrypt runs end to end with AWS KMS as the key provider. It needs no adapter code now: AwsDataKeySource is a vitaminc-kms KeyProvider, FixedIndexKeySource adds the persisted index KeyId, and StaticKeysetRegistry holds the one keyset that StackCipher is built over. The crate stays detached from the root workspace and is listed in the root exclude. An AWS feature on a workspace member would put the AWS SDK into every --all-features build, and the SDK turns on serde_json/preserve_order, which changes JSON map order for the whole workspace. Its vitaminc-kms dependency uses the same git source and rev as stack-encrypt, so there is one KeyProvider trait. The demo now fails when AWS KMS refuses ListKeys, for example because of an IAM policy. Before, any error made it skip and exit with success. It skips only when LocalStack is the target and nothing listens there. LocalStack's compose file moves next to the demo, stays pinned to the Community :4 image, and binds to 127.0.0.1 only, because LocalStack accepts any credentials. The README says how the index KeyId is provisioned once and read back on every run, and the demo can read both ids from the environment to show that. --- Cargo.toml | 4 + examples/aws-kms-demo/.gitignore | 1 + examples/aws-kms-demo/Cargo.lock | 2731 ++++++++++++++++++++++ examples/aws-kms-demo/Cargo.toml | 38 + examples/aws-kms-demo/README.md | 93 + examples/aws-kms-demo/docker-compose.yml | 27 + examples/aws-kms-demo/src/main.rs | 183 ++ 7 files changed, 3077 insertions(+) create mode 100644 examples/aws-kms-demo/.gitignore create mode 100644 examples/aws-kms-demo/Cargo.lock create mode 100644 examples/aws-kms-demo/Cargo.toml create mode 100644 examples/aws-kms-demo/README.md create mode 100644 examples/aws-kms-demo/docker-compose.yml create mode 100644 examples/aws-kms-demo/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 4ad564ff7..98f88ebeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,10 @@ exclude = [ # `wasm:auth-guest:build`. "languages/golang/stackencrypt/guest", "languages/golang/stackauth/guest", + # The AWS KMS demo: detached so the AWS SDK, and the + # `serde_json/preserve_order` it turns on, stay out of this workspace's + # `--all-features` builds. See examples/aws-kms-demo/README.md. + "examples/aws-kms-demo", ] [workspace.package] diff --git a/examples/aws-kms-demo/.gitignore b/examples/aws-kms-demo/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/examples/aws-kms-demo/.gitignore @@ -0,0 +1 @@ +/target diff --git a/examples/aws-kms-demo/Cargo.lock b/examples/aws-kms-demo/Cargo.lock new file mode 100644 index 000000000..ba65fc26a --- /dev/null +++ b/examples/aws-kms-demo/Cargo.lock @@ -0,0 +1,2731 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "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 0.2.17", +] + +[[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", + "zeroize", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-config" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d7b388a9fc3a6db15a5ec778c38b354eff1364882c94d08e0252f7a47dcaa4" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 1.5.0", + "sha1", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93964ffdaf57857f544be3666a5f57570bb699e934700f11b49708f61bb556e" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-kms-demo" +version = "0.0.0" +dependencies = [ + "aws-config", + "aws-sdk-kms", + "stack-encrypt", + "tokio", + "uuid", + "vitaminc-kms", +] + +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "aws-runtime" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8a9911551b4ea6ca13805ef52ed96f7d2bbb43cc3b4a14cb0776a71f33cfaa" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "bytes-utils", + "fastrand", + "http 1.5.0", + "http-body 1.1.0", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bbe8833a7ad3b970f2a6a856939c9cb171942dcdfd2ab65f2db3a8baaf73cc1" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12be2f9c8eef7f5fc919c96d538e629698469a02b4cb75408b26b1bd984ebe79" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7645db8724ea3b82fdccfb67e1b0f637c9d8ab0e7ef29d84884d8d8d73f805d" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.119.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e490aa904849b38e770922faac779dd245f5cf8be81f19065acf18d276e4ad" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2312577f088c9fbf4206dfdb884cf1de9407b43e1a923cbed5237775116fc24b" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 1.5.0", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02e407fb3b54891734224b9ffac8a71fdd35f542500fa1af95754a6b2beb316" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.64.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639b4d8f8555f24a9be649811c3eb0b4d4616f4d61daf0c32e28873bc1ea9af1" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c89cc3f1f281d659a67a519a1b5c6d445b5ce09fa7e5aee40c2c2707e9509d" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.19", + "http 0.2.12", + "http 1.5.0", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.11.1", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.10", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.45", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.6", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.63.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3385d469edbe8b60cc72002784652b5efca39178192aa9cc4b44c9875c6bdc18" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e86338c869539a581bf161247762a6e87f92c5c075060057b5ed6d06632ed0c" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.62.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d1d71f6562be974caa85442ecd90194c40fdb5df045f182a6c2e872ce95056" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-smithy-xml", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e302ac1d88b99652489df31abdec6ac42a2ab2ac3982ad0ac49f64dfaf28ba" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "bytes", + "fastrand", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0730c16f91124c6a2abb4932c77e299288b3dd9f967ea2e9ec48cc6731e87a4" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api-macros", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.5.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-runtime-api-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221eaa237ddf1ca79b60d1372aad77e47f9c0ea5b3ce5099da8c61d027dc77b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "aws-smithy-schema" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8f395d93304280b64b7632fea798d177e74897fe7f063416ce627cd6fa24829" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.5.0", +] + +[[package]] +name = "aws-smithy-types" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69bb407740a197147da48238ecc94498493c9e85445732360cec180296ca45f1" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.5.0", + "http-body 0.4.6", + "http-body 1.1.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.62.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b932c8d6dc127fc980eecd78f8694ae9b9551b69a93a7def2a199c1c0033daf" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209f3a6d82a6e9e5f94abbed94c7a26e1c052341002bf57a5fb5481f625896fc" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" +dependencies = [ + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.1", +] + +[[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-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + +[[package]] +name = "cllw-ore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476f300d37a5029d3d9dd57145d4db50a23f40ae9b4d1374c44543978b906191" +dependencies = [ + "blake3", + "hex", + "subtle", + "thiserror 1.0.69", + "unicode-normalization", + "zeroize", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +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 = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[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", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", + "zeroize", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +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 = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "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", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "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 = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.5.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http 1.5.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http 1.5.0", + "http-body 1.1.0", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", + "httparse", + "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.32", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http 1.5.0", + "hyper 1.11.1", + "hyper-util", + "rustls 0.23.45", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.26.6", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "httparse", + "hyper 1.11.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.5", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mutants" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ac067452ff1aca8c5002111bd6b1c895baee6e45fcbc44e0193aea17be56" + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bee6c73da26345c729282832b60b0363cf3dd9f4bfd81d8551b7a1c889a113" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[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.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.15", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stack-encrypt" +version = "0.3.0" +dependencies = [ + "base64ct", + "cllw-ore", + "serde", + "stack-encrypt-derive", + "thiserror 1.0.69", + "uuid", + "vitaminc-aead", + "vitaminc-encrypt", + "vitaminc-hmac", + "vitaminc-kms", + "vitaminc-prf", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "stack-encrypt-derive" +version = "0.3.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.5", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls 0.23.45", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vitaminc-aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cacd4dae485e98cfe5407e919187a8468dd933ea4234be5115b7d13a30f3c6" +dependencies = [ + "bytes", + "serde", + "vitaminc-aead-derive", + "vitaminc-context", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-aead-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff34056b70a8417ad3bf9db73b2bd3be97f448794566c6de2080bd8a7cd9b6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-async-traits" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-traits", +] + +[[package]] +name = "vitaminc-context" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7df52501fe33cdb3ee9e155b5242fac184c037d147e58c96e5143ed011252a8" +dependencies = [ + "mutants", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vitaminc-encrypt" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbd0e1748055e381e02033e08af385de879c4dcf32e0f6739ac0c0cf0c6f13d" +dependencies = [ + "aes-gcm", + "aws-lc-rs", + "vitaminc-aead", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-hmac" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fe558a5e92d4f2b3f811c5b67d863d9255da5f66d08a71d4f0dd3d0011c563" +dependencies = [ + "hmac", + "sha2", + "vitaminc-prf", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-kms" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "aws-sdk-kms", + "futures", + "thiserror 2.0.21", + "vitaminc-async-traits", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-traits", + "zeroize", +] + +[[package]] +name = "vitaminc-prf" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712d31d46d76b38c6b62deb35f516fc5636a35f90f39c0213d0ca9a052b4400b" +dependencies = [ + "mutants", + "thiserror 2.0.21", + "vitaminc-context", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vitaminc-protected" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7b514f605a63f88874ba46ffea81775fad64994475ab239cb19da159e884b7b" +dependencies = [ + "bitvec", + "digest 0.11.3", + "libc", + "serde", + "serde_bytes", + "subtle", + "thiserror 2.0.21", + "vitaminc-protected-derive 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-protected" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "bitvec", + "digest 0.11.3", + "libc", + "serde", + "serde_bytes", + "subtle", + "thiserror 2.0.21", + "vitaminc-protected-derive 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vitaminc-protected-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb0a3d11afbe2c909f0cfc9996fe37fd89b1196eb0b8d89a66e28716360ee09" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-protected-derive" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-random" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7b1bfab35ee322de37269c6fd94d8cd94047a66f97cf6722e53e3321482167" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random-derives 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-random" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-random-derives 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vitaminc-random-derives" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0915e2bfb417ce11e23123fb7ec122af5d27d8cfd22d6ad6a2cf7a1c2dbb4712" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-random-derives" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-traits" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "anyhow", + "bytes", + "rmp-serde", + "serde", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-random 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.6", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] diff --git a/examples/aws-kms-demo/Cargo.toml b/examples/aws-kms-demo/Cargo.toml new file mode 100644 index 000000000..1a5b4854b --- /dev/null +++ b/examples/aws-kms-demo/Cargo.toml @@ -0,0 +1,38 @@ +# `stack-encrypt` running end to end against AWS KMS, kept out of the root +# cargo workspace on purpose. +# +# A standalone workspace, like the WASI guests under languages/golang and the +# fuzz crates. CI builds the root workspace with `--all-features`, and no +# feature gate can hide a feature from that, so an AWS feature on a workspace +# member puts the AWS SDK in every workspace build. `aws-smithy-http-client` +# turns on `serde_json/preserve_order`, which under feature unification +# changes JSON map ordering for every other crate in the workspace. Detached, +# this crate resolves its own graph, in its own Cargo.lock. The root +# mise.toml's Rust pin applies here too. +# +# Run: docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +# cd examples/aws-kms-demo && mise x -- cargo run +[package] +name = "aws-kms-demo" +description = "stack-encrypt encrypting and decrypting with AWS KMS as the key provider" +version = "0.0.0" +edition = "2021" +publish = false + +[workspace] + +[dependencies] +# `default-features = false`: the demo builds its cipher over its own +# keyset registry, so it needs neither ZeroKMS nor the HTTP transport. +stack-encrypt = { path = "../../packages/stack-encrypt", default-features = false } +# `AwsDataKeySource`. The same git source and the same `rev` spelling as +# stack-encrypt's own dependency, so the two resolve to one package: a +# second copy would be a different `KeyProvider` trait, and its providers +# would not satisfy stack-encrypt's `KeysetRegistry`. Move all of them +# together. +vitaminc-kms = { git = "https://github.com/cipherstash/vitaminc", rev = "b3bc6eb", default-features = false, features = ["aws"] } + +aws-config = "1.8.18" +aws-sdk-kms = "1.109.0" +tokio = { version = "1", features = ["macros", "rt"] } +uuid = "1" diff --git a/examples/aws-kms-demo/README.md b/examples/aws-kms-demo/README.md new file mode 100644 index 000000000..fdad1f8db --- /dev/null +++ b/examples/aws-kms-demo/README.md @@ -0,0 +1,93 @@ +# aws-kms-demo + +`stack-encrypt` encrypting and decrypting a value with **AWS KMS** as the key +provider instead of ZeroKMS. + +The chain has three pieces. None of them is specific to this demo: + +| Piece | Role | +| --- | --- | +| `vitaminc_kms::AwsDataKeySource<32>` | A data key source bound to one AWS KMS key. It is a `vitaminc-kms` `KeyProvider<32>`. | +| `stack_encrypt::registry::FixedIndexKeySource` | Pairs that source with the persisted `KeyId` of the keyset's index key, so it is also an `IndexKeyProvider<32>`. | +| `stack_encrypt::registry::StaticKeysetRegistry` | stack-encrypt's registry over a fixed table of keysets. Here the table has one keyset, which is the default. | + +`StackCipher` is then built over that registry, in the same way as over +ZeroKMS. The same chain works for Azure Key Vault, Cloud KMS and Vault +Transit: turn on the vendor feature of `vitaminc-kms` and use that vendor's +data key source. + +## Running it + +LocalStack Community's `kms` service is sufficient. You do not need a Pro +tier or an AWS account: + +```sh +docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +cd examples/aws-kms-demo && mise x -- cargo run +docker compose -f examples/aws-kms-demo/docker-compose.yml down +``` + +LocalStack binds to `127.0.0.1` only, because it accepts any credentials. + +If nothing listens on `localhost:4566`, the demo prints a skip notice and +exits with success. Every other failure is an error and exits with a +failure. This includes an AWS KMS that refuses the first call (`ListKeys`), +for example because of bad credentials or an IAM policy. + +| Variable | Effect | +| --- | --- | +| `AWS_KMS_DEMO_ENDPOINT_URL` | The KMS endpoint. Unset: LocalStack at `http://localhost:4566`, with fake credentials. Empty: the AWS SDK's own endpoint, region and credentials. | +| `AWS_KMS_DEMO_KEY_ID` | The AWS KMS key that the keyset is bound to. Unset: the demo creates a new symmetric key. | +| `AWS_KMS_DEMO_INDEX_KEY_ID` | The persisted index `KeyId`, as hex. Unset: the demo provisions a new one. It needs `AWS_KMS_DEMO_KEY_ID`. | + +To run against a real AWS account, set `AWS_KMS_DEMO_ENDPOINT_URL` to an empty +string. Supply credentials with `kms:ListKeys`, `kms:GenerateDataKey` and +`kms:Decrypt`, and `kms:CreateKey` if you do not set `AWS_KMS_DEMO_KEY_ID`. + +## How the index key's `KeyId` is provisioned + +stack-encrypt derives each keyset's equality, match and order terms from one +**index key**. On ZeroKMS the service derives that key from the keyset. AWS +KMS has no deterministic derivation, so the index key is an ordinary data key +that is stored once and read back on every run: + +1. **Provision once.** Call `GenerateDataKey` one time on the keyset's AWS + KMS key. Keep only the result's `key_id`. That is the `CiphertextBlob`, + which is the data key that AWS KMS encrypted. Do not keep the plaintext. +2. **Persist the `KeyId`.** Store it with the keyset's configuration, for + example in a configuration file or a secrets store. It is encrypted, but + it is as important as the AWS KMS key itself: if you lose it, all of the + keyset's index terms become unfindable. +3. **Read it back on every run.** Give it to `FixedIndexKeySource::new`. + When the cipher loads the keyset, it decrypts that `KeyId` with AWS KMS, + and the result is the index key. + +Do not provision a new `KeyId` at startup. A new `KeyId` gives a new index +key, and then no index term written before matches a query. + +The demo does step 1 when `AWS_KMS_DEMO_INDEX_KEY_ID` is unset, and it prints +the two values to set for the next run. With both values set, it skips steps +1 and 2 and reads the index key back, as a deployment does. + +The keyset's id (`KEYSET_ID` in `src/main.rs`) is configuration too. Every +leaf sealed under the keyset carries it, so choose it once and keep it with +the AWS KMS key and the index `KeyId`. + +## Why this is a separate crate + +It is detached from the root cargo workspace. Its `Cargo.toml` has its own +`[workspace]` table, the same as `languages/golang/stackencrypt/guest`, and the +root `Cargo.toml` lists it in `exclude`. + +CI builds and tests the root workspace with `--all-features`, and no feature +gate can hide a feature from that. An AWS feature on a workspace member would +put the AWS SDK in *every* workspace build. `aws-smithy-http-client` turns on +`serde_json/preserve_order`, which, under cargo's feature unification, changes +the JSON map order for every other crate in the workspace and breaks snapshot +tests. + +Detached, this crate resolves its own dependency graph in its own +`Cargo.lock`. It builds with the Rust toolchain that the root `mise.toml` +pins. Its `vitaminc-kms` dependency must use the same git source and `rev` as +stack-encrypt's. Otherwise cargo builds two copies, and `AwsDataKeySource` +does not implement the `KeyProvider` trait that stack-encrypt uses. diff --git a/examples/aws-kms-demo/docker-compose.yml b/examples/aws-kms-demo/docker-compose.yml new file mode 100644 index 000000000..1c62ccb47 --- /dev/null +++ b/examples/aws-kms-demo/docker-compose.yml @@ -0,0 +1,27 @@ +# LocalStack for the AWS KMS demo in this folder. +# +# LocalStack Community's `kms` service covers CreateKey, GenerateDataKey, +# Decrypt and ListKeys. No Pro tier is necessary. +# +# Usage: +# docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +# (cd examples/aws-kms-demo && mise x -- cargo run) +# docker compose -f examples/aws-kms-demo/docker-compose.yml down +services: + localstack: + # Pinned to the last Community-only major. `:stable` and `:latest` now + # bundle LocalStack's "unified" image, which stops at startup unless it + # has a paid LOCALSTACK_AUTH_TOKEN, also for services that were free. + # `:4` still runs the plain Community image with no token. + image: localstack/localstack:4 + ports: + # Loopback only: LocalStack accepts any credentials, so it must not + # be reachable from other hosts on the network. + - "127.0.0.1:4566:4566" + environment: + - SERVICES=kms + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"] + interval: 2s + timeout: 5s + retries: 15 diff --git a/examples/aws-kms-demo/src/main.rs b/examples/aws-kms-demo/src/main.rs new file mode 100644 index 000000000..6f1c02438 --- /dev/null +++ b/examples/aws-kms-demo/src/main.rs @@ -0,0 +1,183 @@ +//! `stack-encrypt` running end to end with AWS KMS as the key provider +//! instead of ZeroKMS. +//! +//! The chain is three pieces, and none of them is specific to this demo: +//! +//! - `vitaminc_kms::AwsDataKeySource<32>`: a data key source bound to one +//! AWS KMS key. It is a `vitaminc-kms` `KeyProvider<32>`. +//! - `FixedIndexKeySource`: pairs that source with the persisted `KeyId` of +//! the keyset's index key, so it is also an `IndexKeyProvider<32>`. +//! - `StaticKeysetRegistry`: stack-encrypt's registry over a fixed table of +//! keysets. Here the table has one keyset, which is the default. +//! +//! `StackCipher` is then built over that registry exactly as it is over +//! ZeroKMS. +//! +//! Run it against LocalStack: +//! +//! ```sh +//! docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +//! cd examples/aws-kms-demo && mise x -- cargo run +//! ``` +//! +//! See README.md for the environment variables, for how to run it against a +//! real AWS account, and for how the index key's `KeyId` is provisioned. + +use std::error::Error; + +use aws_sdk_kms::error::{DisplayErrorContext, SdkError}; +use stack_encrypt::registry::{FixedIndexKeySource, KeyId, StaticKeyset, StaticKeysetRegistry}; +use stack_encrypt::{nonempty, StackCipherBuilder}; +use uuid::Uuid; +use vitaminc_kms::{AwsDataKeySource, GenerateDataKey}; + +/// AES-256, the only data key size stack-encrypt uses. +const KEY_SIZE: usize = 32; + +/// The keyset's id. Every leaf sealed under the keyset carries it, so a real +/// deployment chooses it once and keeps it with the keyset's configuration. +const KEYSET_ID: Uuid = Uuid::from_u128(0x6177_732d_6b6d_732d_6465_6d6f_0000_0001); + +const LOCALSTACK: &str = "http://localhost:4566"; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), Box> { + let Some(client) = build_client().await? else { + println!("LocalStack is not running on {LOCALSTACK}; skipping. See README.md."); + return Ok(()); + }; + + // An index KeyId is a data key that one AWS KMS key encrypted. With no + // key named, the demo would create a new one, which cannot decrypt it. + if env("AWS_KMS_DEMO_INDEX_KEY_ID").is_some() && env("AWS_KMS_DEMO_KEY_ID").is_none() { + return Err( + "AWS_KMS_DEMO_INDEX_KEY_ID needs the AWS_KMS_DEMO_KEY_ID it was made under".into(), + ); + } + + // The AWS KMS key the keyset is bound to. + let kms_key_id = match env("AWS_KMS_DEMO_KEY_ID") { + Some(key_id) => key_id, + None => { + let key_id = create_symmetric_key(&client).await?; + println!("created AWS KMS key {key_id}"); + key_id + } + }; + let source = AwsDataKeySource::::new(client, kms_key_id.clone()); + + // The index key's `KeyId`. Provisioned once per keyset and then read + // back on every run: a new one makes every earlier index term + // unfindable. See README.md. + let index_key_id = match env("AWS_KMS_DEMO_INDEX_KEY_ID") { + Some(hex) => KeyId::new(unhex(&hex)?), + None => { + let key_id = source.generate_data_key().await?.key_id; + println!( + "provisioned an index key. To keep this keyset's index terms, run again with\n \ + AWS_KMS_DEMO_KEY_ID={kms_key_id}\n \ + AWS_KMS_DEMO_INDEX_KEY_ID={}", + hex(key_id.as_bytes()) + ); + key_id + } + }; + + let registry = StaticKeysetRegistry::new(StaticKeyset::new( + KEYSET_ID, + FixedIndexKeySource::new(source, index_key_id), + )); + let cipher = StackCipherBuilder::new().registry(registry).init().await?; + println!("built a StackCipher over AWS KMS"); + + let sealed = cipher + .default_keyset() + .encrypt("hello from AWS KMS".to_string(), nonempty!("demo/greeting")) + .await?; + let opened: String = cipher.decrypt(sealed, nonempty!("demo/greeting")).await?; + assert_eq!(opened, "hello from AWS KMS"); + println!("round-tripped a value through stack-encrypt and AWS KMS: {opened:?}"); + + Ok(()) +} + +/// An environment variable, or `None` when it is unset or empty. +fn env(name: &str) -> Option { + std::env::var(name).ok().filter(|value| !value.is_empty()) +} + +/// The AWS KMS client, or `None` when the demo targets LocalStack and +/// nothing is listening there. +/// +/// Only that case is a skip. Any answer from KMS that refuses the call (bad +/// credentials, an IAM policy without `kms:ListKeys`) is an error, and so is +/// an unreachable endpoint the caller chose: either means the demo cannot +/// run as configured, and exiting with success would hide it. +async fn build_client() -> Result, Box> { + use aws_config::{BehaviorVersion, Region}; + + // Unset: LocalStack. Empty: the SDK's own endpoint and credentials. + let endpoint_url = + std::env::var("AWS_KMS_DEMO_ENDPOINT_URL").unwrap_or_else(|_| LOCALSTACK.to_string()); + let localstack = endpoint_url == LOCALSTACK; + + let mut loader = aws_config::defaults(BehaviorVersion::v2026_01_12()); + if !endpoint_url.is_empty() { + // A local endpoint: fixed region, and fake static credentials, + // which LocalStack accepts. + let credentials = aws_sdk_kms::config::Credentials::new("fake", "fake", None, None, "demo"); + loader = loader + .region(Region::new("us-east-1")) + .credentials_provider(credentials) + .endpoint_url(endpoint_url); + } + let client = aws_sdk_kms::Client::new(&loader.load().await); + + // A cheap call that fails fast, before the demo creates anything. + match client.list_keys().limit(1).send().await { + Ok(_) => Ok(Some(client)), + Err(SdkError::DispatchFailure(_) | SdkError::TimeoutError(_)) if localstack => Ok(None), + Err(error) => Err(format!( + "AWS KMS refused or did not answer ListKeys: {}", + DisplayErrorContext(&error) + ) + .into()), + } +} + +async fn create_symmetric_key(client: &aws_sdk_kms::Client) -> Result> { + use aws_sdk_kms::types::{KeySpec, KeyUsageType}; + + // `EncryptDecrypt` usage and a symmetric spec are what GenerateDataKey + // and Decrypt, the two calls `AwsDataKeySource` makes, require. + let key = client + .create_key() + .key_usage(KeyUsageType::EncryptDecrypt) + .key_spec(KeySpec::SymmetricDefault) + .send() + .await + .map_err(|error| format!("CreateKey failed: {}", DisplayErrorContext(&error)))?; + + Ok(key + .key_metadata + .map(|metadata| metadata.key_id) + .ok_or("AWS KMS did not return key metadata")?) +} + +fn hex(bytes: &[u8]) -> String { + bytes.iter().map(|byte| format!("{byte:02x}")).collect() +} + +fn unhex(hex: &str) -> Result, Box> { + if !hex.len().is_multiple_of(2) { + return Err("AWS_KMS_DEMO_INDEX_KEY_ID must be hex, two digits a byte".into()); + } + (0..hex.len()) + .step_by(2) + .map(|i| { + hex.get(i..i + 2) + .and_then(|pair| u8::from_str_radix(pair, 16).ok()) + .ok_or_else(|| "AWS_KMS_DEMO_INDEX_KEY_ID must be hex".into()) + }) + .collect() +} From 52c93ceb85e4067db8a13a02064ccfda13b336d4 Mon Sep 17 00:00:00 2001 From: James Sadler Date: Tue, 6 Oct 2026 22:40:46 +1100 Subject: [PATCH 3/3] docs(stack-encrypt): say how a vendor backend reaches a cipher, and changelog the static registry A reader of the crate docs saw ZeroKMS and the fake registry only. The new section says how AWS KMS, Azure Key Vault, Cloud KMS and Vault Transit reach a cipher: a vitaminc-kms data key source, FixedIndexKeySource with the persisted index KeyId, and StaticKeysetRegistry. It also says why that vitaminc-kms must be the same copy as this crate's, and points at the AWS demo. --- packages/stack-encrypt/CHANGELOG.md | 9 +++++++++ packages/stack-encrypt/src/lib.rs | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/packages/stack-encrypt/CHANGELOG.md b/packages/stack-encrypt/CHANGELOG.md index b838190ac..a4e2d2c72 100644 --- a/packages/stack-encrypt/CHANGELOG.md +++ b/packages/stack-encrypt/CHANGELOG.md @@ -250,6 +250,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `NoRegistry`, and the `vitaminc-kms` provider types an implementor names. - `StackCipher::registry`, and `FakeKeysetRegistry::resolves` (a counter of keyset lookups) for tests. +- **`registry::StaticKeysetRegistry`: keysets on AWS KMS, Azure Key Vault, + Cloud KMS or Vault Transit.** A fixed table of keysets, each an id, an + optional name and a key provider. The first keyset is the default; a + keyset resolves by id, or by name if it has one, and anything else is + `Error::UnknownKeyset`. It does not read format-1 leaves. Build each + provider as `registry::FixedIndexKeySource` (now re-exported) over a + `vitaminc-kms` data key source and the persisted `KeyId` of the keyset's + index key. `StaticKeyset`, `SharedProvider` and `StaticRegistryError` come + with it. `examples/aws-kms-demo` runs it on AWS KMS. ## [0.2.0] - 2026-10-04 diff --git a/packages/stack-encrypt/src/lib.rs b/packages/stack-encrypt/src/lib.rs index 9f7d9bd55..033b785b5 100644 --- a/packages/stack-encrypt/src/lib.rs +++ b/packages/stack-encrypt/src/lib.rs @@ -175,6 +175,19 @@ endpoint — are `StackKmsBuilder`'s, and the two keyset-cache knobs are "# )] //! +//! # Other key providers +//! +//! ZeroKMS is one backend. AWS KMS, Azure Key Vault, Cloud KMS and Vault +//! Transit reach a cipher as `vitaminc-kms` key providers: depend on +//! `vitaminc-kms` with the vendor's feature, wrap the vendor's data key +//! source in [`registry::FixedIndexKeySource`] with the persisted `KeyId` of +//! the keyset's index key, and put each keyset in a +//! [`registry::StaticKeysetRegistry`]. That `vitaminc-kms` must be the same +//! copy this crate depends on (the same git source and `rev` until it is on +//! crates.io), or its providers implement a different `KeyProvider` trait. +//! `examples/aws-kms-demo` at the repository root runs this end to end on +//! AWS KMS, and its README says how the index `KeyId` is provisioned. +//! //! # Testing without ZeroKMS //! //! `stack_encrypt::registry::fake::FakeKeysetRegistry` is an in-memory stub that needs no