opentdf_protocol/nanotdf/
policy.rs1use crate::binary::{BinaryRead, BinaryWrite, read_u8, read_u16_be, write_u8, write_u16_be};
4use crate::nanotdf::resource_locator::ResourceLocator;
5use std::io::{self, Read, Write};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum PolicyType {
11 Remote = 0x00,
13 EmbeddedPlaintext = 0x01,
15 EmbeddedEncrypted = 0x02,
17 EmbeddedEncryptedPolicyKeyAccess = 0x03,
19}
20
21impl PolicyType {
22 pub fn from_byte(byte: u8) -> io::Result<Self> {
24 match byte {
25 0x00 => Ok(PolicyType::Remote),
26 0x01 => Ok(PolicyType::EmbeddedPlaintext),
27 0x02 => Ok(PolicyType::EmbeddedEncrypted),
28 0x03 => Ok(PolicyType::EmbeddedEncryptedPolicyKeyAccess),
29 _ => Err(io::Error::new(
30 io::ErrorKind::InvalidData,
31 format!("Invalid policy type: 0x{:02X}", byte),
32 )),
33 }
34 }
35
36 pub fn to_byte(self) -> u8 {
38 self as u8
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum PolicyBody {
45 Remote(ResourceLocator),
47 EmbeddedPlaintext(Vec<u8>),
49 EmbeddedEncrypted(Vec<u8>),
51 EmbeddedEncryptedWithKeyAccess {
53 content: Vec<u8>,
55 key_access: ResourceLocator,
57 ephemeral_key: Vec<u8>,
59 },
60}
61
62impl PolicyBody {
63 pub fn policy_type(&self) -> PolicyType {
65 match self {
66 PolicyBody::Remote(_) => PolicyType::Remote,
67 PolicyBody::EmbeddedPlaintext(_) => PolicyType::EmbeddedPlaintext,
68 PolicyBody::EmbeddedEncrypted(_) => PolicyType::EmbeddedEncrypted,
69 PolicyBody::EmbeddedEncryptedWithKeyAccess { .. } => {
70 PolicyType::EmbeddedEncryptedPolicyKeyAccess
71 }
72 }
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Policy {
88 pub body: PolicyBody,
90 pub binding: Vec<u8>,
95}
96
97impl Policy {
98 pub fn new(body: PolicyBody, binding: Vec<u8>) -> Self {
100 Self { body, binding }
101 }
102
103 pub fn remote(locator: ResourceLocator, binding: Vec<u8>) -> Self {
105 Self::new(PolicyBody::Remote(locator), binding)
106 }
107
108 pub fn embedded_plaintext(content: Vec<u8>, binding: Vec<u8>) -> Self {
110 Self::new(PolicyBody::EmbeddedPlaintext(content), binding)
111 }
112
113 pub fn embedded_encrypted(content: Vec<u8>, binding: Vec<u8>) -> Self {
115 Self::new(PolicyBody::EmbeddedEncrypted(content), binding)
116 }
117}
118
119impl BinaryRead for Policy {
120 fn read_from<R: Read>(reader: &mut R) -> io::Result<Self> {
121 let policy_type = PolicyType::from_byte(read_u8(reader)?)?;
123
124 let body = match policy_type {
126 PolicyType::Remote => {
127 let locator = ResourceLocator::read_from(reader)?;
128 PolicyBody::Remote(locator)
129 }
130 PolicyType::EmbeddedPlaintext | PolicyType::EmbeddedEncrypted => {
131 let content_len = read_u16_be(reader)? as usize;
133
134 let mut content = vec![0u8; content_len];
136 reader.read_exact(&mut content)?;
137
138 if policy_type == PolicyType::EmbeddedPlaintext {
139 PolicyBody::EmbeddedPlaintext(content)
140 } else {
141 PolicyBody::EmbeddedEncrypted(content)
142 }
143 }
144 PolicyType::EmbeddedEncryptedPolicyKeyAccess => {
145 let content_len = read_u16_be(reader)? as usize;
147
148 let mut content = vec![0u8; content_len];
150 reader.read_exact(&mut content)?;
151
152 let key_access = ResourceLocator::read_from(reader)?;
154
155 let mut ephemeral_key = vec![0u8; 33];
160 reader.read_exact(&mut ephemeral_key)?;
161
162 PolicyBody::EmbeddedEncryptedWithKeyAccess {
163 content,
164 key_access,
165 ephemeral_key,
166 }
167 }
168 };
169
170 let mut binding = vec![0u8; 8];
175 reader.read_exact(&mut binding)?;
176
177 Ok(Self { body, binding })
178 }
179}
180
181impl BinaryWrite for Policy {
182 fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
183 write_u8(writer, self.body.policy_type().to_byte())?;
185
186 match &self.body {
188 PolicyBody::Remote(locator) => {
189 locator.write_to(writer)?;
190 }
191 PolicyBody::EmbeddedPlaintext(content) | PolicyBody::EmbeddedEncrypted(content) => {
192 if content.len() > 0xFFFF {
194 return Err(io::Error::new(
195 io::ErrorKind::InvalidInput,
196 format!(
197 "Policy content too large: {} bytes (max 65535)",
198 content.len()
199 ),
200 ));
201 }
202
203 write_u16_be(writer, content.len() as u16)?;
205
206 writer.write_all(content)?;
208 }
209 PolicyBody::EmbeddedEncryptedWithKeyAccess {
210 content,
211 key_access,
212 ephemeral_key,
213 } => {
214 if content.len() > 0xFFFF {
216 return Err(io::Error::new(
217 io::ErrorKind::InvalidInput,
218 format!(
219 "Policy content too large: {} bytes (max 65535)",
220 content.len()
221 ),
222 ));
223 }
224
225 write_u16_be(writer, content.len() as u16)?;
227
228 writer.write_all(content)?;
230
231 key_access.write_to(writer)?;
233
234 writer.write_all(ephemeral_key)?;
236 }
237 }
238
239 writer.write_all(&self.binding)?;
241
242 Ok(())
243 }
244
245 fn serialized_size(&self) -> usize {
246 let body_size = match &self.body {
247 PolicyBody::Remote(locator) => locator.serialized_size(),
248 PolicyBody::EmbeddedPlaintext(content) | PolicyBody::EmbeddedEncrypted(content) => {
249 2 + content.len() }
251 PolicyBody::EmbeddedEncryptedWithKeyAccess {
252 content,
253 key_access,
254 ephemeral_key,
255 } => 2 + content.len() + key_access.serialized_size() + ephemeral_key.len(),
256 };
257
258 1 + body_size +
260 self.binding.len()
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::nanotdf::resource_locator::Protocol;
268
269 #[test]
270 fn test_remote_policy() {
271 let locator = ResourceLocator::new(Protocol::Https, b"kas.example.com/policy/123");
272 let binding = vec![0u8; 8]; let policy = Policy::remote(locator, binding);
274
275 assert!(matches!(policy.body, PolicyBody::Remote(_)));
276 assert_eq!(policy.body.policy_type(), PolicyType::Remote);
277 }
278
279 #[test]
280 fn test_embedded_plaintext_policy() {
281 let content = b"policy content".to_vec();
282 let binding = vec![0u8; 8];
283 let policy = Policy::embedded_plaintext(content.clone(), binding);
284
285 match &policy.body {
286 PolicyBody::EmbeddedPlaintext(c) => assert_eq!(c, &content),
287 _ => panic!("Expected EmbeddedPlaintext"),
288 }
289 }
290
291 #[test]
292 fn test_policy_roundtrip() {
293 let content = b"test policy".to_vec();
294 let binding = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
295 let original = Policy::embedded_plaintext(content, binding);
296
297 let mut buf = Vec::new();
298 original.write_to(&mut buf).unwrap();
299
300 assert!(!buf.is_empty());
303 }
304}