From 8230f39bd819b701a7ce3d60f5adc16f535f479b Mon Sep 17 00:00:00 2001 From: James Sadler Date: Fri, 6 Nov 2026 22:11:22 +1100 Subject: [PATCH 1/2] refactor(guest): hold no key vendor backend as None, not a fake one GuestVendor::None was a vendor that was not there. It said its vendor was Static, it offered no key mode, and every call failed. It existed only because KeyRouter needed a vendor, even for a client with none. KeyRouter now holds an Option of the vendor key source. KeyRouter::zerokms_only makes a router with no vendor, and it sends every request to ZeroKMS. KeyRouter::vendor returns an Option. The guest builds a vendor key source only when the config names a backend, and picks new or zerokms_only from that. GuestVendor has no None variant, and the dispatch has no arm for it. A config with no ZeroKMS key and no backend does not parse, and cipher_init refuses it again rather than build a client with no key source. The routing does not change: with no vendor, every request went to ZeroKMS before, and it does now. A new router test shows it. --- .changeset/kms-router-optional-vendor.md | 5 + languages/golang/encrypt/guest/src/abi.rs | 39 +++-- languages/golang/encrypt/guest/src/vendor.rs | 76 +++------ packages/stack-kms-vendor/src/router.rs | 155 +++++++++++-------- packages/stack-kms-vendor/tests/router.rs | 23 +++ 5 files changed, 175 insertions(+), 123 deletions(-) create mode 100644 .changeset/kms-router-optional-vendor.md diff --git a/.changeset/kms-router-optional-vendor.md b/.changeset/kms-router-optional-vendor.md new file mode 100644 index 000000000..6fc2ea6bc --- /dev/null +++ b/.changeset/kms-router-optional-vendor.md @@ -0,0 +1,5 @@ +--- +'@cipherstash/auth': patch +--- + +Internal change to the key vendor crates that `keysetCreate` and `keysetRewrap` compile in. The key router can now hold no key vendor backend. There is no change to what `keysetCreate` and `keysetRewrap` do. diff --git a/languages/golang/encrypt/guest/src/abi.rs b/languages/golang/encrypt/guest/src/abi.rs index d7ff5642c..90138010d 100644 --- a/languages/golang/encrypt/guest/src/abi.rs +++ b/languages/golang/encrypt/guest/src/abi.rs @@ -111,7 +111,8 @@ use stack_encrypt::ErrorPayload; /// ZeroKMS is the host-transport client with host-supplied tokens — or, in /// the `deterministic-kms` test build, the seeded source the record fixture /// was sealed under. The vendor is [`GuestVendor`], over the step loop; with -/// none configured it holds no keyset and every request goes to ZeroKMS. +/// none configured the router has no vendor and every request goes to +/// ZeroKMS. #[cfg(not(feature = "deterministic-kms"))] type GuestZeroKms = crate::zerokms::MaybeZeroKms< stack_kms::StackKms, @@ -235,15 +236,23 @@ fn cipher_init(config: InitConfig) -> Result, u32> { return Err(init_out_of_order()); } - let vendor = build_vendor(config.vendor)?; - let kms = match config.client_key { + let vendor = config.vendor.map(build_vendor).transpose()?; + let kms = match (config.client_key, vendor) { // A key vendor backend and no ZeroKMS (CIP-5311): no ZeroKMS client // is built, no token is asked for, and the default keyset is the // vendor's. - None => { + (None, Some(vendor)) => { stack_kms_vendor::KeyRouter::new(MaybeZeroKms::Absent, vendor).with_vendor_default() } - Some(client_key) => { + // `parse_config` asks for the ZeroKMS keys when there is no key + // vendor backend, so this config does not parse. Refuse it here + // too, rather than build a client with no key source. + (None, None) => { + return Err(malformed( + "the config has no ZeroKMS client key and no key vendor backend", + )) + } + (Some(client_key), vendor) => { // One request at a time: a step hands the host one request, and the // first version of the step loop keeps it that way. Raising it later // changes no ABI: a step's request list can hold several. @@ -271,7 +280,7 @@ fn cipher_init(config: InitConfig) -> Result, u32> { STATUS_KMS_TRANSPORT, ) })?; - stack_kms_vendor::KeyRouter::new(MaybeZeroKms::Present(zerokms), vendor) + router(MaybeZeroKms::Present(zerokms), vendor) } }; let mut builder = StackCipher::builder().kms(kms); @@ -311,9 +320,9 @@ fn cipher_init(config: InitConfig) -> Result, u32> { "the deterministic build's default keyset is the seeded source's", )); } - let kms = stack_kms_vendor::KeyRouter::new( + let kms = router( crate::zerokms::MaybeZeroKms::Present(config.seed.source()), - build_vendor(config.vendor)?, + config.vendor.map(build_vendor).transpose()?, ); let builder = StackCipher::builder().kms(kms); let initialising = Initialising::begin(); @@ -353,6 +362,18 @@ impl Drop for Initialising { } } +/// The key source of a client with ZeroKMS: ZeroKMS and the key vendor +/// backend, or ZeroKMS alone when none is configured. +fn router( + zerokms: GuestZeroKms, + vendor: Option>, +) -> GuestKms { + match vendor { + Some(vendor) => stack_kms_vendor::KeyRouter::new(zerokms, vendor), + None => stack_kms_vendor::KeyRouter::zerokms_only(zerokms), + } +} + /// Build the configured key vendor backend over the step loop: its requests, /// credentials and backoff waits all pause the operation for the host (no /// vendor call here: records are parsed and checked, keys are unwrapped on @@ -360,7 +381,7 @@ impl Drop for Initialising { /// `STATUS_ENCODING` naming its config key; a refused keyset record keeps /// its error for `se_last_error` ([`crate::status::fail_record`]). fn build_vendor( - config: Option, + config: crate::vendor::VendorConfig, ) -> Result, u32> { use crate::vendor::BuildError; crate::vendor::build( diff --git a/languages/golang/encrypt/guest/src/vendor.rs b/languages/golang/encrypt/guest/src/vendor.rs index 0de144584..7e4edc388 100644 --- a/languages/golang/encrypt/guest/src/vendor.rs +++ b/languages/golang/encrypt/guest/src/vendor.rs @@ -6,9 +6,10 @@ //! //! * [`GuestVendor`] is one enum over the four vendor clients and the static //! key backend, so the cipher has one key source type whatever the host -//! chose: `KeyRouter`. With no backend configured it -//! is [`GuestVendor::None`] with no keyset records, and the router sends -//! every request to ZeroKMS, as before. +//! chose: `KeyRouter`. With no backend configured +//! there is no `GuestVendor`: the router is +//! `KeyRouter::zerokms_only`, and sends every request to ZeroKMS, as +//! before. //! * The vendor clients send through the host's HTTP client //! (`StepVendorTransport`: each request, and each backoff wait, pauses the //! operation through the step loop) and sign each request here, in the @@ -400,9 +401,6 @@ impl stack_kms_azure::TokenProvider for HostCredentials /// One enum, not a type parameter, so that the instance's cipher has one /// type whatever the host chose at run time. pub enum GuestVendor { - /// No key vendor backend. It holds no keyset records, so the router - /// never calls it, and every method refuses. - None, Aws(stack_kms_aws::AwsKms>), Vault(stack_kms_vault::VaultTransit>), Gcp(stack_kms_gcp::GcpKms>), @@ -413,7 +411,6 @@ pub enum GuestVendor { impl std::fmt::Debug for GuestVendor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { - Self::None => "none", Self::Aws(_) => KeyVendor::Aws.name(), Self::Vault(_) => KeyVendor::Vault.name(), Self::Gcp(_) => KeyVendor::Gcp.name(), @@ -424,18 +421,10 @@ impl std::fmt::Debug for GuestVendor { } } -/// Every method of [`GuestVendor::None`]: it holds no keyset, so a call is -/// a routing bug, refused rather than answered. It is not a backend -/// failure, because there is no backend to name. -fn no_vendor() -> Error { - Error::Unexpected("no key vendor backend is configured".into()) -} - /// Dispatch one `KeyVendorBackend` method to the configured client. macro_rules! dispatch { - ($self:expr, $backend:ident => $call:expr, none => $none:expr) => { + ($self:expr, $backend:ident => $call:expr) => { match $self { - GuestVendor::None => $none, GuestVendor::Aws($backend) => $call, GuestVendor::Vault($backend) => $call, GuestVendor::Gcp($backend) => $call, @@ -450,23 +439,20 @@ where T: VendorTransport + Send + Sync, C: CredentialImport, { - /// The configured vendor. [`GuestVendor::None`] answers `Static`: it is - /// never asked, because it holds no keyset (the router asks a backend - /// only for a keyset one of its records names). fn vendor(&self) -> KeyVendor { - dispatch!(self, b => b.vendor(), none => KeyVendor::Static) + dispatch!(self, b => b.vendor()) } fn supports_mode(&self, mode: KeyMode) -> bool { - dispatch!(self, b => b.supports_mode(mode), none => false) + dispatch!(self, b => b.supports_mode(mode)) } async fn wrap(&self, key_ref: &str, key: &KeyBytes, aad: &[u8]) -> Result, Error> { - dispatch!(self, b => b.wrap(key_ref, key, aad).await, none => Err(no_vendor())) + dispatch!(self, b => b.wrap(key_ref, key, aad).await) } async fn unwrap(&self, key_ref: &str, wrapped: &[u8], aad: &[u8]) -> Result { - dispatch!(self, b => b.unwrap(key_ref, wrapped, aad).await, none => Err(no_vendor())) + dispatch!(self, b => b.unwrap(key_ref, wrapped, aad).await) } async fn generate_wrapped( @@ -474,11 +460,11 @@ where key_ref: &str, aad: &[u8], ) -> Result<(KeyBytes, Vec), Error> { - dispatch!(self, b => b.generate_wrapped(key_ref, aad).await, none => Err(no_vendor())) + dispatch!(self, b => b.generate_wrapped(key_ref, aad).await) } fn batch_limit(&self) -> Option { - dispatch!(self, b => b.batch_limit(), none => None) + dispatch!(self, b => b.batch_limit()) } async fn wrap_batch( @@ -486,7 +472,7 @@ where key_ref: &str, items: &[WrapItem<'_>], ) -> Result>, Error> { - dispatch!(self, b => b.wrap_batch(key_ref, items).await, none => Err(no_vendor())) + dispatch!(self, b => b.wrap_batch(key_ref, items).await) } async fn unwrap_batch( @@ -494,11 +480,11 @@ where key_ref: &str, items: &[UnwrapItem<'_>], ) -> Result, Error> { - dispatch!(self, b => b.unwrap_batch(key_ref, items).await, none => Err(no_vendor())) + dispatch!(self, b => b.unwrap_batch(key_ref, items).await) } async fn sleep(&self, duration: Duration) { - dispatch!(self, b => b.sleep(duration).await, none => ()) + dispatch!(self, b => b.sleep(duration).await) } } @@ -520,10 +506,11 @@ impl From for BuildError { } /// Build the key source of the configured backend: the client over -/// `transport` and `import`, and the adapter over the keyset records. With -/// no config, [`GuestVendor::None`] and no records. +/// `transport` and `import`, and the adapter over the keyset records. A +/// config with no backend has no key source to build: the caller routes +/// every request to ZeroKMS. pub fn build( - config: Option, + config: VendorConfig, transport: T, import: C, ) -> Result>, BuildError> @@ -531,15 +518,12 @@ where T: VendorTransport + Send + Sync, C: CredentialImport, { - let Some(VendorConfig { + let VendorConfig { settings, records, default_keyset, root_key_ttl, - }) = config - else { - return Ok(VendorKeySource::new(GuestVendor::None, [])?); - }; + } = config; let credentials = HostCredentials::new(import); let backend = match settings { VendorSettings::Aws { region, endpoint } => { @@ -870,7 +854,7 @@ mod tests { ("session_token", string("fixture-session-token")), ])) }); - let source = build(Some(aws_settings()), &transport, import).unwrap(); + let source = build(aws_settings(), &transport, import).unwrap(); let key = block(source.backend().unwrap( "arn:aws:kms:us-east-1:111122223333:key/k", b"wrapped", @@ -894,16 +878,6 @@ mod tests { ); } - #[test] - fn no_config_builds_no_vendor_and_no_keyset() { - let transport = Recording::default(); - let source = - build::<&Recording, FakeImport>(None, &transport, FakeImport::unused()).unwrap(); - assert!(matches!(source.backend(), GuestVendor::None)); - assert!(block(source.backend().unwrap("k", b"w", b"a")).is_err()); - assert!(!source.backend().supports_mode(KeyMode::Derived)); - } - #[test] fn a_bad_setting_names_its_config_key() { let config = VendorConfig { @@ -915,7 +889,7 @@ mod tests { default_keyset: None, root_key_ttl: None, }; - let error = build(Some(config), &Recording::default(), FakeImport::unused()).unwrap_err(); + let error = build(config, &Recording::default(), FakeImport::unused()).unwrap_err(); assert!(matches!(error, BuildError::Setting("aws_region"))); let config = VendorConfig { @@ -928,7 +902,7 @@ mod tests { default_keyset: None, root_key_ttl: None, }; - let error = build(Some(config), &Recording::default(), FakeImport::unused()).unwrap_err(); + let error = build(config, &Recording::default(), FakeImport::unused()).unwrap_err(); assert!(matches!(error, BuildError::Setting("vault_address"))); } @@ -945,7 +919,7 @@ mod tests { default_keyset: None, root_key_ttl: None, }; - let error = build(Some(config), &Recording::default(), FakeImport::unused()).unwrap_err(); + let error = build(config, &Recording::default(), FakeImport::unused()).unwrap_err(); assert!(matches!( error, BuildError::Record(RecordError::ForeignVendor { .. }) @@ -956,7 +930,7 @@ mod tests { ..aws_settings() }; assert!(matches!( - build(Some(config), &Recording::default(), FakeImport::unused(),), + build(config, &Recording::default(), FakeImport::unused(),), Err(BuildError::Record(_)) )); } diff --git a/packages/stack-kms-vendor/src/router.rs b/packages/stack-kms-vendor/src/router.rs index c4cb550c0..59507722e 100644 --- a/packages/stack-kms-vendor/src/router.rs +++ b/packages/stack-kms-vendor/src/router.rs @@ -12,12 +12,13 @@ use uuid::Uuid; use crate::source::VendorKeySource; use crate::vendor::KeyVendorBackend; -/// ZeroKMS and one key vendor backend, as one key source that routes each -/// request by its keyset (ADR-0009). +/// ZeroKMS and at most one key vendor backend, as one key source that +/// routes each request by its keyset (ADR-0009). /// /// A cipher holds ZeroKMS and **at most one** key vendor backend. The type -/// has room for exactly one, and [`VendorKeySource::new`] refuses records -/// that name a second vendor +/// has room for one or none: [`new`](Self::new) gives it one, and +/// [`zerokms_only`](Self::zerokms_only) none. [`VendorKeySource::new`] +/// refuses records that name a second vendor /// ([`RecordError::SecondVendor`](crate::RecordError::SecondVendor)). /// /// Routing: @@ -29,6 +30,7 @@ use crate::vendor::KeyVendorBackend; /// other name goes to ZeroKMS. /// * Naming no keyset is ZeroKMS's default keyset, or the vendor's default /// keyset after [`with_vendor_default`](Self::with_vendor_default). +/// * With no vendor, every request goes to ZeroKMS. /// /// The cipher reads [`keyset_backend`](DataKeySource::keyset_backend) to /// refuse a leaf that names the wrong backend before any key call, and to @@ -36,7 +38,7 @@ use crate::vendor::KeyVendorBackend; /// between the two backends in either direction. pub struct KeyRouter { zerokms: Z, - vendor: VendorKeySource, + vendor: Option>, vendor_default: bool, } @@ -55,7 +57,17 @@ impl KeyRouter { pub fn new(zerokms: Z, vendor: VendorKeySource) -> Self { Self { zerokms, - vendor, + vendor: Some(vendor), + vendor_default: false, + } + } + + /// Send every request to `zerokms`: a client with no key vendor + /// backend. + pub fn zerokms_only(zerokms: Z) -> Self { + Self { + zerokms, + vendor: None, vendor_default: false, } } @@ -64,7 +76,8 @@ impl KeyRouter { /// record, or the one set with /// [`VendorKeySource::with_default_keyset`]), not ZeroKMS's. For a /// client that has a key vendor backend and no ZeroKMS credentials: its - /// ZeroKMS side is then a source that refuses every request. + /// ZeroKMS side is then a source that refuses every request. A router + /// with no vendor ([`zerokms_only`](Self::zerokms_only)) ignores it. pub fn with_vendor_default(mut self) -> Self { self.vendor_default = true; self @@ -75,23 +88,40 @@ impl KeyRouter { &self.zerokms } - /// The key vendor backend's key source. - pub fn vendor(&self) -> &VendorKeySource { - &self.vendor + /// The key vendor backend's key source, or `None` when the router has + /// no vendor. + pub fn vendor(&self) -> Option<&VendorKeySource> { + self.vendor.as_ref() + } + + /// The vendor, when it is the default. + fn default_vendor(&self) -> Option<&VendorKeySource> { + self.vendor.as_ref().filter(|_| self.vendor_default) + } + + /// The vendor, when one of its records names `keyset`. + fn vendor_with_keyset(&self, keyset: Uuid) -> Option<&VendorKeySource> { + self.vendor + .as_ref() + .filter(|vendor| vendor.has_keyset(keyset)) } - fn is_vendor_id(&self, keyset: Option) -> bool { + /// The vendor, when `keyset` routes to it. + fn vendor_for_id(&self, keyset: Option) -> Option<&VendorKeySource> { match keyset { - None => self.vendor_default, - Some(keyset) => self.vendor.has_keyset(keyset), + None => self.default_vendor(), + Some(keyset) => self.vendor_with_keyset(keyset), } } - fn is_vendor_selection(&self, keyset: Option<&IdentifiedBy>) -> bool { + /// The vendor, when the selection `keyset` routes to it. + fn vendor_for_selection(&self, keyset: Option<&IdentifiedBy>) -> Option<&VendorKeySource> { match keyset { - None => self.vendor_default, - Some(IdentifiedBy::Uuid(id)) => self.vendor.has_keyset(*id), - Some(IdentifiedBy::Name(name)) => self.vendor.has_name(name), + None => self.default_vendor(), + Some(IdentifiedBy::Uuid(id)) => self.vendor_with_keyset(*id), + Some(IdentifiedBy::Name(name)) => { + self.vendor.as_ref().filter(|vendor| vendor.has_name(name)) + } } } } @@ -107,14 +137,17 @@ where keyset_id: Option, unverified_context: Option>, ) -> Result, Error> { - if self.is_vendor_id(keyset_id) { - self.vendor - .generate_keys(payloads, keyset_id, unverified_context) - .await - } else { - self.zerokms - .generate_keys(payloads, keyset_id, unverified_context) - .await + match self.vendor_for_id(keyset_id) { + Some(vendor) => { + vendor + .generate_keys(payloads, keyset_id, unverified_context) + .await + } + None => { + self.zerokms + .generate_keys(payloads, keyset_id, unverified_context) + .await + } } } @@ -124,49 +157,48 @@ where keyset_id: Option, unverified_context: Option<&UnverifiedContext>, ) -> Result, Error> { - if self.is_vendor_id(keyset_id) { - self.vendor - .retrieve_keys(payloads, keyset_id, unverified_context) - .await - } else { - self.zerokms - .retrieve_keys(payloads, keyset_id, unverified_context) - .await + match self.vendor_for_id(keyset_id) { + Some(vendor) => { + vendor + .retrieve_keys(payloads, keyset_id, unverified_context) + .await + } + None => { + self.zerokms + .retrieve_keys(payloads, keyset_id, unverified_context) + .await + } } } /// The backend of the default keyset: ZeroKMS, or the vendor after - /// [`with_vendor_default`](Self::with_vendor_default). Per keyset, see - /// [`keyset_backend`](DataKeySource::keyset_backend). + /// [`with_vendor_default`](KeyRouter::with_vendor_default). Per keyset, + /// see [`keyset_backend`](DataKeySource::keyset_backend). fn key_backend(&self) -> KeyBackend { - if self.vendor_default { - self.vendor.key_backend() - } else { - self.zerokms.key_backend() + match self.default_vendor() { + Some(vendor) => vendor.key_backend(), + None => self.zerokms.key_backend(), } } fn keyset_backend(&self, keyset_id: Uuid) -> KeyBackend { - if self.vendor.has_keyset(keyset_id) { - self.vendor.key_backend() - } else { - self.zerokms.keyset_backend(keyset_id) + match self.vendor_with_keyset(keyset_id) { + Some(vendor) => vendor.key_backend(), + None => self.zerokms.keyset_backend(keyset_id), } } fn key_mode(&self, keyset_id: Uuid) -> Option { - if self.vendor.has_keyset(keyset_id) { - self.vendor.key_mode(keyset_id) - } else { - self.zerokms.key_mode(keyset_id) + match self.vendor_with_keyset(keyset_id) { + Some(vendor) => vendor.key_mode(keyset_id), + None => self.zerokms.key_mode(keyset_id), } } async fn load_settle_index_key(&self, keyset_id: Uuid) -> Result { - if self.vendor.has_keyset(keyset_id) { - self.vendor.load_settle_index_key(keyset_id).await - } else { - self.zerokms.load_settle_index_key(keyset_id).await + match self.vendor_with_keyset(keyset_id) { + Some(vendor) => vendor.load_settle_index_key(keyset_id).await, + None => self.zerokms.load_settle_index_key(keyset_id).await, } } } @@ -180,10 +212,9 @@ where &self, keyset_id: Option, ) -> Result<(Uuid, IndexKey), Error> { - if self.is_vendor_selection(keyset_id.as_ref()) { - self.vendor.load_index_key(keyset_id).await - } else { - self.zerokms.load_index_key(keyset_id).await + match self.vendor_for_selection(keyset_id.as_ref()) { + Some(vendor) => vendor.load_index_key(keyset_id).await, + None => self.zerokms.load_index_key(keyset_id).await, } } @@ -191,10 +222,9 @@ where &self, keyset_id: Option, ) -> Result { - if self.is_vendor_selection(keyset_id.as_ref()) { - self.vendor.load_index_key_with_lifetime(keyset_id).await - } else { - self.zerokms.load_index_key_with_lifetime(keyset_id).await + match self.vendor_for_selection(keyset_id.as_ref()) { + Some(vendor) => vendor.load_index_key_with_lifetime(keyset_id).await, + None => self.zerokms.load_index_key_with_lifetime(keyset_id).await, } } @@ -202,10 +232,9 @@ where &self, keyset_id: Option, ) -> Result { - if self.is_vendor_selection(keyset_id.as_ref()) { - self.vendor.select_keyset(keyset_id).await - } else { - self.zerokms.select_keyset(keyset_id).await + match self.vendor_for_selection(keyset_id.as_ref()) { + Some(vendor) => vendor.select_keyset(keyset_id).await, + None => self.zerokms.select_keyset(keyset_id).await, } } } diff --git a/packages/stack-kms-vendor/tests/router.rs b/packages/stack-kms-vendor/tests/router.rs index 9c79ffc73..114ffbf9b 100644 --- a/packages/stack-kms-vendor/tests/router.rs +++ b/packages/stack-kms-vendor/tests/router.rs @@ -378,3 +378,26 @@ async fn a_vendor_default_router_selects_the_vendor_for_no_keyset() { let opened: String = cipher.decrypt(sealed, context).await.unwrap(); assert_eq!(opened, "v"); } + +/// A router with no vendor sends every request to ZeroKMS, the default +/// keyset included, even when it is told to prefer the vendor. +#[tokio::test] +async fn a_router_with_no_vendor_sends_everything_to_zerokms() { + let router = + KeyRouter::<_, FakeVendor>::zerokms_only(FakeDataKeySource::new()).with_vendor_default(); + assert!(router.vendor().is_none()); + assert_eq!( + stack_encrypt::kms::DataKeySource::key_backend(&router), + KeyBackend::ZeroKms + ); + let cipher = StackCipher::builder().kms(router).init().await.unwrap(); + let context = nonempty!("users").with("email"); + let sealed = cipher + .default_keyset() + .encrypt("z".to_string(), context) + .await + .unwrap(); + assert_eq!(leaf_backend(&sealed), (KeyBackend::ZeroKms, None)); + let opened: String = cipher.decrypt(sealed, context).await.unwrap(); + assert_eq!(opened, "z"); +} From 3f8e2ef4d105773a9a7856359f580b5d27fa0b8f Mon Sep 17 00:00:00 2001 From: James Sadler Date: Mon, 9 Nov 2026 13:46:53 +1100 Subject: [PATCH 2/2] test(stack-kms-vendor): seal through KeysetCipher::encrypt_tree in the router tests --- packages/stack-kms-vendor/tests/router.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stack-kms-vendor/tests/router.rs b/packages/stack-kms-vendor/tests/router.rs index 114ffbf9b..a691dec0d 100644 --- a/packages/stack-kms-vendor/tests/router.rs +++ b/packages/stack-kms-vendor/tests/router.rs @@ -394,7 +394,7 @@ async fn a_router_with_no_vendor_sends_everything_to_zerokms() { let context = nonempty!("users").with("email"); let sealed = cipher .default_keyset() - .encrypt("z".to_string(), context) + .encrypt_tree("z".to_string(), context) .await .unwrap(); assert_eq!(leaf_backend(&sealed), (KeyBackend::ZeroKms, None));