fraiseql_core/security/kms/
models.rs1use std::{collections::HashMap, fmt};
7
8use serde::{Deserialize, Serialize};
9use zeroize::Zeroizing;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14#[non_exhaustive]
15pub enum KeyPurpose {
16 EncryptDecrypt,
18 SignVerify,
20 Mac,
22}
23
24impl fmt::Display for KeyPurpose {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::EncryptDecrypt => write!(f, "encrypt_decrypt"),
28 Self::SignVerify => write!(f, "sign_verify"),
29 Self::Mac => write!(f, "mac"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37#[non_exhaustive]
38pub enum KeyState {
39 Enabled,
41 Disabled,
43 PendingDeletion,
45 Destroyed,
47}
48
49impl fmt::Display for KeyState {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::Enabled => write!(f, "enabled"),
53 Self::Disabled => write!(f, "disabled"),
54 Self::PendingDeletion => write!(f, "pending_deletion"),
55 Self::Destroyed => write!(f, "destroyed"),
56 }
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct KeyReference {
63 pub provider: String,
65 pub key_id: String,
67 pub key_alias: Option<String>,
69 pub purpose: KeyPurpose,
71 pub created_at: i64,
73}
74
75impl KeyReference {
76 pub const fn new(
78 provider: String,
79 key_id: String,
80 purpose: KeyPurpose,
81 created_at: i64,
82 ) -> Self {
83 Self {
84 provider,
85 key_id,
86 key_alias: None,
87 purpose,
88 created_at,
89 }
90 }
91
92 #[must_use]
94 pub fn with_alias(mut self, alias: String) -> Self {
95 self.key_alias = Some(alias);
96 self
97 }
98
99 #[must_use]
101 pub fn qualified_id(&self) -> String {
102 format!("{}:{}", self.provider, self.key_id)
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct EncryptedData {
109 pub ciphertext: String,
111 pub key_reference: KeyReference,
113 pub algorithm: String,
115 pub encrypted_at: i64,
117 pub context: HashMap<String, String>,
119}
120
121impl EncryptedData {
122 pub const fn new(
124 ciphertext: String,
125 key_reference: KeyReference,
126 algorithm: String,
127 encrypted_at: i64,
128 context: HashMap<String, String>,
129 ) -> Self {
130 Self {
131 ciphertext,
132 key_reference,
133 algorithm,
134 encrypted_at,
135 context,
136 }
137 }
138}
139
140#[derive(Debug, Clone)]
142pub struct DataKeyPair {
143 pub plaintext_key: Zeroizing<Vec<u8>>,
145 pub encrypted_key: EncryptedData,
147 pub key_reference: KeyReference,
149}
150
151impl DataKeyPair {
152 pub fn new(
154 plaintext_key: Vec<u8>,
155 encrypted_key: EncryptedData,
156 key_reference: KeyReference,
157 ) -> Self {
158 Self {
159 plaintext_key: Zeroizing::new(plaintext_key),
160 encrypted_key,
161 key_reference,
162 }
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct RotationPolicy {
169 pub enabled: bool,
171 pub rotation_period_days: u32,
173 pub last_rotation: Option<i64>,
175 pub next_rotation: Option<i64>,
177}
178
179impl RotationPolicy {
180 pub const fn new(enabled: bool, rotation_period_days: u32) -> Self {
182 Self {
183 enabled,
184 rotation_period_days,
185 last_rotation: None,
186 next_rotation: None,
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_key_reference_qualified_id() {
197 let key_ref = KeyReference::new(
198 "vault".to_string(),
199 "my-key-123".to_string(),
200 KeyPurpose::EncryptDecrypt,
201 1_000_000,
202 );
203 assert_eq!(key_ref.qualified_id(), "vault:my-key-123");
204 }
205
206 #[test]
207 fn test_key_reference_with_alias() {
208 let key_ref = KeyReference::new(
209 "vault".to_string(),
210 "my-key-123".to_string(),
211 KeyPurpose::EncryptDecrypt,
212 1_000_000,
213 )
214 .with_alias("production-key".to_string());
215
216 assert_eq!(key_ref.key_alias, Some("production-key".to_string()));
217 }
218
219 #[test]
220 fn test_key_purpose_display() {
221 assert_eq!(KeyPurpose::EncryptDecrypt.to_string(), "encrypt_decrypt");
222 assert_eq!(KeyPurpose::SignVerify.to_string(), "sign_verify");
223 assert_eq!(KeyPurpose::Mac.to_string(), "mac");
224 }
225
226 #[test]
227 fn test_key_state_display() {
228 assert_eq!(KeyState::Enabled.to_string(), "enabled");
229 assert_eq!(KeyState::Disabled.to_string(), "disabled");
230 }
231
232 #[test]
233 fn test_rotation_policy_new() {
234 let policy = RotationPolicy::new(true, 90);
235 assert!(policy.enabled);
236 assert_eq!(policy.rotation_period_days, 90);
237 assert_eq!(policy.last_rotation, None);
238 assert_eq!(policy.next_rotation, None);
239 }
240}