1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use parking_lot::RwLock;
5
6pub type SharedKmsState = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<KmsState>>>;
7
8impl fakecloud_core::multi_account::AccountState for KmsState {
9 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
10 Self::new(account_id, region)
11 }
12}
13
14#[derive(Clone, serde::Serialize, serde::Deserialize)]
15pub struct KmsState {
16 pub account_id: String,
17 pub region: String,
18 pub keys: BTreeMap<String, KmsKey>,
19 pub aliases: BTreeMap<String, KmsAlias>,
20 pub grants: Vec<KmsGrant>,
21 pub custom_key_stores: BTreeMap<String, CustomKeyStore>,
22 #[serde(default = "default_master_key_bytes")]
28 pub master_key_bytes: Vec<u8>,
29 #[serde(default)]
34 pub import_wrapping_keys: BTreeMap<String, ImportWrapEntry>,
35}
36
37#[derive(Clone, serde::Serialize, serde::Deserialize)]
38pub struct ImportWrapEntry {
39 pub private_key_der: Vec<u8>,
44 pub key_id: String,
47}
48
49fn default_master_key_bytes() -> Vec<u8> {
50 use aes_gcm::aead::rand_core::RngCore;
51 use aes_gcm::aead::OsRng;
52 let mut bytes = vec![0u8; 32];
53 OsRng.fill_bytes(&mut bytes);
54 bytes
55}
56
57impl KmsState {
58 pub fn new(account_id: &str, region: &str) -> Self {
59 Self {
60 account_id: account_id.to_string(),
61 region: region.to_string(),
62 keys: BTreeMap::new(),
63 aliases: BTreeMap::new(),
64 grants: Vec::new(),
65 custom_key_stores: BTreeMap::new(),
66 master_key_bytes: default_master_key_bytes(),
67 import_wrapping_keys: BTreeMap::new(),
68 }
69 }
70
71 pub fn reset(&mut self) {
72 self.keys.clear();
73 self.aliases.clear();
74 self.grants.clear();
75 self.custom_key_stores.clear();
76 }
78}
79
80#[derive(Clone, serde::Serialize, serde::Deserialize)]
81pub struct KmsKey {
82 pub key_id: String,
83 pub arn: String,
84 pub creation_date: f64,
85 pub description: String,
86 pub enabled: bool,
87 pub key_usage: String,
88 pub key_spec: String,
89 pub key_manager: String,
90 pub key_state: String,
91 pub deletion_date: Option<f64>,
92 pub tags: BTreeMap<String, String>,
93 pub policy: String,
94 pub key_rotation_enabled: bool,
95 pub origin: String,
96 pub multi_region: bool,
97 pub rotations: Vec<KeyRotation>,
98 pub signing_algorithms: Option<Vec<String>>,
99 pub encryption_algorithms: Option<Vec<String>>,
100 pub mac_algorithms: Option<Vec<String>>,
101 pub custom_key_store_id: Option<String>,
102 pub imported_key_material: bool,
103 pub imported_material_bytes: Option<Vec<u8>>,
105 pub private_key_seed: Vec<u8>,
107 pub primary_region: Option<String>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub asymmetric_private_key_der: Option<Vec<u8>>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub asymmetric_public_key_der: Option<Vec<u8>>,
117}
118
119#[derive(Clone, serde::Serialize, serde::Deserialize)]
120pub struct KmsAlias {
121 pub alias_name: String,
122 pub alias_arn: String,
123 pub target_key_id: String,
124 pub creation_date: f64,
125}
126
127#[derive(Clone, serde::Serialize, serde::Deserialize)]
128pub struct KmsGrant {
129 pub grant_id: String,
130 pub grant_token: String,
131 pub key_id: String,
132 pub grantee_principal: String,
133 pub retiring_principal: Option<String>,
134 pub operations: Vec<String>,
135 pub constraints: Option<serde_json::Value>,
136 pub name: Option<String>,
137 pub creation_date: f64,
138}
139
140#[derive(Clone, serde::Serialize, serde::Deserialize)]
141pub struct KeyRotation {
142 pub key_id: String,
143 pub rotation_date: f64,
144 pub rotation_type: String,
145}
146
147#[derive(Clone, serde::Serialize, serde::Deserialize)]
148pub struct CustomKeyStore {
149 pub custom_key_store_id: String,
150 pub custom_key_store_name: String,
151 pub custom_key_store_type: String,
152 pub cloud_hsm_cluster_id: Option<String>,
153 pub trust_anchor_certificate: Option<String>,
154 pub connection_state: String,
155 pub creation_date: f64,
156 pub xks_proxy_uri_endpoint: Option<String>,
157 pub xks_proxy_uri_path: Option<String>,
158 pub xks_proxy_vpc_endpoint_service_name: Option<String>,
159 pub xks_proxy_connectivity: Option<String>,
160}
161
162#[derive(Clone, serde::Serialize, serde::Deserialize)]
165pub struct KmsSnapshot {
166 pub schema_version: u32,
167 #[serde(default)]
168 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<KmsState>>,
169 #[serde(default)]
170 pub state: Option<KmsState>,
171}
172
173pub const KMS_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn new_has_empty_collections() {
181 let state = KmsState::new("123456789012", "us-east-1");
182 assert_eq!(state.account_id, "123456789012");
183 assert_eq!(state.region, "us-east-1");
184 assert!(state.keys.is_empty());
185 assert!(state.aliases.is_empty());
186 assert!(state.grants.is_empty());
187 assert!(state.custom_key_stores.is_empty());
188 }
189
190 #[test]
191 fn reset_clears_collections() {
192 let mut state = KmsState::new("123456789012", "us-east-1");
193 state.aliases.insert(
194 "alias/test".to_string(),
195 KmsAlias {
196 alias_name: "alias/test".to_string(),
197 alias_arn: "arn".to_string(),
198 target_key_id: "k".to_string(),
199 creation_date: 0.0,
200 },
201 );
202 assert!(!state.aliases.is_empty());
203 state.reset();
204 assert!(state.aliases.is_empty());
205 }
206}