ironcore_documents/v5/
attached.rs1use bytes::{Buf, Bytes};
2use protobuf::Message;
3
4use crate::{Error, aes::IvAndCiphertext, icl_header_v4::V4DocumentHeader};
5
6use super::key_id_header::{self, KeyIdHeader};
7
8type Result<A> = std::result::Result<A, Error>;
9
10#[derive(Debug, PartialEq)]
11pub struct AttachedDocument {
12 pub key_id_header: KeyIdHeader,
13 pub edek: V4DocumentHeader,
14 pub edoc: IvAndCiphertext,
15}
16
17impl TryFrom<Vec<u8>> for AttachedDocument {
18 type Error = Error;
19
20 fn try_from(value: Vec<u8>) -> Result<Self> {
22 Bytes::from(value).try_into()
23 }
24}
25
26impl TryFrom<Bytes> for AttachedDocument {
27 type Error = Error;
28
29 fn try_from(value: Bytes) -> Result<Self> {
31 let (key_id_header, mut attached_document_with_header) =
32 key_id_header::decode_version_prefixed_value(value)?;
33 if attached_document_with_header.len() > 2 {
34 let edek_len = attached_document_with_header.get_u16();
35 if attached_document_with_header.len() > edek_len as usize {
36 let header_bytes = attached_document_with_header.split_to(edek_len as usize);
37 let edek = protobuf::Message::parse_from_bytes(&header_bytes[..])
38 .map_err(|e| Error::HeaderParseErr(e.to_string()))?;
39 Ok(AttachedDocument {
40 key_id_header,
41 edek,
42 edoc: IvAndCiphertext(attached_document_with_header),
43 })
44 } else {
45 Err(Error::HeaderParseErr(
46 "The EDEK you passed in was too short based on the length bytes.".to_string(),
47 ))
48 }
49 } else {
50 Err(Error::HeaderParseErr("Header is too short.".to_string()))
51 }
52 }
53}
54
55impl AttachedDocument {
56 pub fn write_to_bytes(&self) -> Result<Bytes> {
58 let AttachedDocument {
59 key_id_header,
60 edek,
61 edoc,
62 } = self;
63 let key_id_header_bytes = key_id_header.write_to_bytes();
64 let encoded_edek = edek.write_to_bytes().expect("Writing to bytes is safe");
65 if encoded_edek.len() > u16::MAX as usize {
66 Err(Error::HeaderLengthOverflow(encoded_edek.len() as u64))
67 } else {
68 let len = encoded_edek.len() as u16;
69
70 let result = [
71 key_id_header_bytes.as_ref(),
72 &len.to_be_bytes(),
73 &encoded_edek,
74 &edoc.0, ]
76 .concat();
77 Ok(result.into())
78 }
79 }
80}
81
82#[cfg(test)]
83mod test {
84 use crate::{
85 Error,
86 aes::IvAndCiphertext,
87 icl_header_v4::{
88 V4DocumentHeader,
89 v4document_header::{
90 EdekWrapper, SignedPayload,
91 edek_wrapper::{Aes256GcmEncryptedDek, Edek},
92 },
93 },
94 v5::key_id_header::{EdekType, KeyId, KeyIdHeader, PayloadType},
95 };
96
97 use super::*;
98
99 #[test]
100 fn test_roundtrip() {
101 let key_id_header = KeyIdHeader::new(
102 EdekType::SaasShield,
103 PayloadType::StandardEdek,
104 KeyId(u32::MAX),
105 );
106
107 let edek_wrapper = EdekWrapper {
108 edek: Some(Edek::Aes256GcmEdek(Aes256GcmEncryptedDek {
109 ciphertext: [42u8; 255].as_ref().into(),
110 ..Default::default()
111 })),
112 ..Default::default()
113 };
114
115 let edek = V4DocumentHeader {
116 signed_payload: Some(SignedPayload {
117 edeks: vec![edek_wrapper],
118 ..Default::default()
119 })
120 .into(),
121 ..Default::default()
122 };
123
124 let edoc = IvAndCiphertext(vec![100, 200, 0].into());
125 let expected_result = AttachedDocument {
126 key_id_header,
127 edek,
128 edoc,
129 };
130 let encoded = expected_result.write_to_bytes().unwrap();
131 let result: AttachedDocument = encoded.try_into().unwrap();
132 assert_eq!(result, expected_result);
133 }
134
135 #[test]
136 fn test_len_too_long() {
137 let encoded = vec![
139 255u8, 255, 255, 255, 2, 0, 0, 255, 18, 7, 18, 5, 26, 3, 18, 1, 42, 100, 200,
140 ];
141 let result = AttachedDocument::try_from(encoded).unwrap_err();
142 assert_eq!(
143 result,
144 Error::HeaderParseErr(
145 "The EDEK you passed in was too short based on the length bytes.".to_string()
146 )
147 );
148 }
149
150 #[test]
151 fn test_header_too_short() {
152 let encoded = vec![255u8, 255, 255, 255, 2, 0, 0];
154 let result = AttachedDocument::try_from(encoded).unwrap_err();
155 assert_eq!(
156 result,
157 Error::HeaderParseErr("Header is too short.".to_string())
158 );
159 }
160}