iceberg/spec/
encrypted_key.rs1use std::collections::HashMap;
19
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, PartialEq, Eq, typed_builder::TypedBuilder)]
26pub struct EncryptedKey {
27 #[builder(setter(into))]
29 pub(crate) key_id: String,
30 #[builder(setter(into))]
32 pub(crate) encrypted_key_metadata: Vec<u8>,
33 #[builder(default, setter(into, strip_option))]
35 pub(crate) encrypted_by_id: Option<String>,
36 #[builder(default)]
38 pub(crate) properties: HashMap<String, String>,
39}
40
41impl EncryptedKey {
42 pub fn key_id(&self) -> &str {
44 &self.key_id
45 }
46
47 pub fn encrypted_key_metadata(&self) -> &[u8] {
49 &self.encrypted_key_metadata
50 }
51
52 pub fn encrypted_by_id(&self) -> Option<&str> {
54 self.encrypted_by_id.as_deref()
55 }
56
57 pub fn properties(&self) -> &HashMap<String, String> {
59 &self.properties
60 }
61}
62
63pub(super) mod _serde {
64 use base64::Engine as _;
65 use base64::engine::general_purpose::STANDARD as BASE64;
66
67 use super::*;
68
69 #[derive(Serialize, Deserialize)]
71 #[serde(rename_all = "kebab-case")]
72 pub(super) struct EncryptedKeySerde {
73 pub key_id: String,
74 pub encrypted_key_metadata: String, pub encrypted_by_id: Option<String>,
76 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
77 pub properties: HashMap<String, String>,
78 }
79
80 impl From<&EncryptedKey> for EncryptedKeySerde {
81 fn from(key: &EncryptedKey) -> Self {
82 Self {
83 key_id: key.key_id.clone(),
84 encrypted_key_metadata: BASE64.encode(&key.encrypted_key_metadata),
85 encrypted_by_id: key.encrypted_by_id.clone(),
86 properties: key.properties.clone(),
87 }
88 }
89 }
90
91 impl TryFrom<EncryptedKeySerde> for EncryptedKey {
92 type Error = base64::DecodeError;
93
94 fn try_from(serde_key: EncryptedKeySerde) -> Result<Self, Self::Error> {
95 let encrypted_key_metadata = BASE64.decode(&serde_key.encrypted_key_metadata)?;
96
97 Ok(Self {
98 key_id: serde_key.key_id,
99 encrypted_key_metadata,
100 encrypted_by_id: serde_key.encrypted_by_id,
101 properties: serde_key.properties,
102 })
103 }
104 }
105}
106
107impl Serialize for EncryptedKey {
108 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
109 where S: serde::Serializer {
110 let serde_key = _serde::EncryptedKeySerde::from(self);
111 serde_key.serialize(serializer)
112 }
113}
114
115impl<'de> Deserialize<'de> for EncryptedKey {
116 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117 where D: serde::Deserializer<'de> {
118 let serde_key = _serde::EncryptedKeySerde::deserialize(deserializer)?;
119
120 Self::try_from(serde_key).map_err(serde::de::Error::custom)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use serde_json::json;
127
128 use super::*;
129
130 #[test]
131 fn test_encrypted_key_serialization() {
132 let metadata = b"iceberg";
134 let mut properties = HashMap::new();
135 properties.insert("algo".to_string(), "AES-256".to_string());
136 properties.insert("created-at".to_string(), "2023-05-15T10:30:00Z".to_string());
137
138 let key = EncryptedKey::builder()
140 .key_id("5f819b")
141 .encrypted_key_metadata(metadata.to_vec())
142 .encrypted_by_id("user-456")
143 .properties(properties)
144 .build();
145
146 let serialized = serde_json::to_value(&key).unwrap();
148
149 let expected = json!({
150 "key-id": "5f819b",
151 "encrypted-key-metadata": "aWNlYmVyZw==",
152 "encrypted-by-id": "user-456",
153 "properties": {
154 "algo": "AES-256",
155 "created-at": "2023-05-15T10:30:00Z"
156 }
157 });
158 assert_eq!(serialized, expected);
159 }
160
161 #[test]
162 fn test_encrypted_key_round_trip() {
163 let metadata = b"binary\0data\xff\xfe with special bytes";
165 let mut properties = HashMap::new();
166 properties.insert("algo".to_string(), "AES-256".to_string());
167
168 let original_key = EncryptedKey::builder()
170 .key_id("key-abc")
171 .encrypted_key_metadata(metadata.to_vec())
172 .encrypted_by_id("service-xyz")
173 .properties(properties)
174 .build();
175
176 let json_string = serde_json::to_string(&original_key).unwrap();
178
179 let deserialized_key: EncryptedKey = serde_json::from_str(&json_string).unwrap();
181
182 assert_eq!(deserialized_key, original_key);
184 assert_eq!(deserialized_key.encrypted_key_metadata(), metadata);
185 }
186
187 #[test]
188 fn test_encrypted_key_empty_properties() {
189 let key = EncryptedKey::builder()
191 .key_id("key-123")
192 .encrypted_key_metadata(b"data".to_vec())
193 .encrypted_by_id("user-456")
194 .build();
195
196 let serialized = serde_json::to_value(&key).unwrap();
198
199 assert!(!serialized.as_object().unwrap().contains_key("properties"));
201
202 let deserialized: EncryptedKey = serde_json::from_value(serialized).unwrap();
204 assert_eq!(deserialized.properties().len(), 0);
205 }
206
207 #[test]
208 fn test_invalid_base64() {
209 let json_value = json!({
211 "key-id": "key-123",
212 "encrypted-key-metadata": "invalid@base64",
213 "encrypted-by-id": "user-456"
214 });
215
216 let result: Result<EncryptedKey, _> = serde_json::from_value(json_value);
218 assert!(result.is_err());
219 }
220}