1use crate::check::Check;
5use crate::container::CHUNK_FLAG_ENCRYPTED;
6use crate::error::{Error, Result};
7use crate::io::{Reader, Writer};
8
9pub const EXTD_FRAME_LEN: usize = 8;
11
12pub const VENDOR_ID_SHIFT: u32 = 8;
19pub const VENDOR_ID_MASK: u32 = 0xFFFF << VENDOR_ID_SHIFT;
21pub const VENDOR_ID_ORA: u16 = 0x0000;
23pub const CRITICAL_BIT: u32 = 1 << 24;
25pub const EXTD_RESERVED_MASK: u32 = (0xFF & !crate::container::CHUNK_FLAG_ENCRYPTED) | (0x7F << 25);
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Extension {
31 pub ext_version: u32,
33 pub ext_type: [u8; 4],
35 pub vendor_id: u16,
37 pub critical: bool,
39 pub encrypted: bool,
41 pub data: Vec<u8>,
43}
44
45impl Extension {
46 pub fn parse(payload: &[u8], flags: u32) -> Result<Extension> {
48 let mut r = Reader::checked(payload, Check::ExtdFrame);
49 let ext_version = r.u32()?;
50 let ext_type = r.array::<4>()?;
51 if !ext_type.is_ascii() {
52 return Err(Error::new(
53 Check::ExtdExtType,
54 format!("ext_type {ext_type:02x?} is not four ASCII characters"),
55 ));
56 }
57 if flags & EXTD_RESERVED_MASK != 0 {
58 return Err(Error::new(
59 Check::ExtdFlags,
60 format!("extension flags {flags:#010x} set a reserved bit"),
61 ));
62 }
63 Ok(Extension {
64 ext_version,
65 ext_type,
66 vendor_id: ((flags & VENDOR_ID_MASK) >> VENDOR_ID_SHIFT) as u16,
67 critical: flags & CRITICAL_BIT != 0,
68 encrypted: flags & CHUNK_FLAG_ENCRYPTED != 0,
69 data: r.rest().to_vec(),
70 })
71 }
72
73 pub fn to_bytes(&self) -> Vec<u8> {
75 let mut w = Writer::with_capacity(EXTD_FRAME_LEN + self.data.len());
76 w.u32(self.ext_version);
77 w.bytes(&self.ext_type);
78 w.bytes(&self.data);
79 w.into_vec()
80 }
81
82 pub fn to_flags(&self) -> u32 {
84 let mut flags = u32::from(self.vendor_id) << VENDOR_ID_SHIFT;
85 if self.critical {
86 flags |= CRITICAL_BIT;
87 }
88 if self.encrypted {
89 flags |= CHUNK_FLAG_ENCRYPTED;
90 }
91 flags
92 }
93
94 pub fn tag(&self) -> String {
96 String::from_utf8_lossy(&self.ext_type).into_owned()
97 }
98
99 pub fn is_implemented(&self) -> bool {
105 false
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn extension_flags_round_trip() {
115 let flags = (0x00ABu32 << VENDOR_ID_SHIFT) | CRITICAL_BIT | CHUNK_FLAG_ENCRYPTED;
116 let mut payload = Vec::new();
117 payload.extend_from_slice(&1u32.to_le_bytes());
118 payload.extend_from_slice(b"SIGN");
119 payload.extend_from_slice(&[9, 8, 7]);
120 let ext = Extension::parse(&payload, flags).unwrap();
121 assert_eq!(ext.ext_version, 1);
122 assert_eq!(&ext.ext_type, b"SIGN");
123 assert_eq!(ext.vendor_id, 0x00AB);
124 assert!(ext.critical);
125 assert!(ext.encrypted);
126 assert_eq!(ext.data, vec![9, 8, 7]);
127 assert_eq!(ext.to_flags(), flags);
128 assert_eq!(ext.tag(), "SIGN");
129
130 let reparsed = Extension::parse(&ext.to_bytes(), ext.to_flags()).unwrap();
131 assert_eq!(reparsed, ext);
132 }
133
134 #[test]
135 fn extension_rejects_bad_frames_types_and_flags() {
136 assert_eq!(
137 Extension::parse(&[1, 2, 3], 0).unwrap_err().check(),
138 Check::ExtdFrame
139 );
140 let non_ascii = [1u32.to_le_bytes(), [0xC3, 0xA9, 0x00, 0x00]].concat();
141 assert_eq!(
142 Extension::parse(&non_ascii, 0).unwrap_err().check(),
143 Check::ExtdExtType
144 );
145 let payload = [1u32.to_le_bytes(), *b"TEST"].concat();
146 assert_eq!(
147 Extension::parse(&payload, 1 << 0).unwrap_err().check(),
148 Check::ExtdFlags
149 );
150 assert_eq!(
151 Extension::parse(&payload, 1 << 25).unwrap_err().check(),
152 Check::ExtdFlags
153 );
154 assert!(Extension::parse(&payload, CHUNK_FLAG_ENCRYPTED).is_ok());
156 }
157}