From e648f683bffdefbd535805d65f16de722f5d9a6d Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Sat, 5 Sep 2026 07:21:25 +0800 Subject: [PATCH] fix: use BTreeMap for deterministic encryption context serialization (#7154) SealScope::encryption_context() returned a HashMap whose key order is non-deterministic. The FakeSealer test round-trips the context through JSON serialization, and HashMap's random iteration order caused the prefix comparison to intermittently fail with 'encryption context mismatch'. Switch to BTreeMap which guarantees stable key ordering. --- crates/ecstore/src/bucket/sealed_credentials.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ecstore/src/bucket/sealed_credentials.rs b/crates/ecstore/src/bucket/sealed_credentials.rs index e6b487136..46a0f79fb 100644 --- a/crates/ecstore/src/bucket/sealed_credentials.rs +++ b/crates/ecstore/src/bucket/sealed_credentials.rs @@ -28,7 +28,7 @@ use async_trait::async_trait; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::fmt; use std::sync::{Arc, OnceLock}; @@ -81,8 +81,8 @@ impl SealScope { /// The encryption context handed to the sealer. Keys are stable: they are /// part of the on-disk contract, because a ciphertext only decrypts under /// the same context. - pub fn encryption_context(&self) -> HashMap { - HashMap::from([ + pub fn encryption_context(&self) -> BTreeMap { + BTreeMap::from([ ("rustfs:store".to_string(), self.store.as_str().to_string()), ("rustfs:owner".to_string(), self.owner.clone()), ("rustfs:field".to_string(), self.field.to_string()), @@ -212,7 +212,7 @@ mod tests { /// with, and refuses a ciphertext presented under a different one. #[derive(Default)] struct FakeSealer { - sealed_contexts: Mutex>>, + sealed_contexts: Mutex>>, } #[async_trait]