1use std::borrow::Cow;
2use std::convert::TryFrom;
3use std::fmt;
4
5use derive_builder::Builder;
6use shorthand::ShortHand;
7
8use crate::attribute::AttributePairs;
9use crate::types::{
10 EncryptionMethod, InitializationVector, KeyFormat, KeyFormatVersions, ProtocolVersion,
11};
12use crate::utils::{quote, unquote};
13use crate::{Error, RequiredVersion};
14
15#[derive(ShortHand, Builder, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
17#[builder(setter(into), build_fn(validate = "Self::validate"))]
18#[shorthand(enable(skip, must_use, into))]
19#[non_exhaustive]
20pub struct DecryptionKey<'a> {
21 pub method: EncryptionMethod,
42 #[builder(setter(into, strip_option), default)]
48 #[shorthand(disable(skip))]
49 pub(crate) uri: Cow<'a, str>,
50 #[builder(setter(into, strip_option), default)]
60 pub iv: InitializationVector,
61 #[builder(setter(into, strip_option), default)]
72 pub format: Option<KeyFormat<'a>>,
73 #[builder(setter(into, strip_option), default)]
81 pub versions: Option<KeyFormatVersions>,
82}
83
84impl<'a> DecryptionKey<'a> {
85 #[must_use]
97 #[inline]
98 pub fn new<I: Into<Cow<'a, str>>>(method: EncryptionMethod, uri: I) -> Self {
99 Self {
100 method,
101 uri: uri.into(),
102 iv: InitializationVector::default(),
103 format: None,
104 versions: None,
105 }
106 }
107
108 #[must_use]
128 #[inline]
129 pub fn builder() -> DecryptionKeyBuilder<'a> {
130 DecryptionKeyBuilder::default()
131 }
132
133 #[must_use]
140 pub fn into_owned(self) -> DecryptionKey<'static> {
141 DecryptionKey {
142 method: self.method,
143 uri: Cow::Owned(self.uri.into_owned()),
144 iv: self.iv,
145 format: self.format.map(|f| f.into_owned()),
146 versions: self.versions,
147 }
148 }
149}
150
151impl RequiredVersion for DecryptionKey<'_> {
157 fn required_version(&self) -> ProtocolVersion {
158 if self.format.is_some() || self.versions.is_some() {
159 ProtocolVersion::V5
160 } else if self.iv.is_some() {
161 ProtocolVersion::V2
162 } else {
163 ProtocolVersion::V1
164 }
165 }
166}
167
168impl<'a> TryFrom<&'a str> for DecryptionKey<'a> {
169 type Error = Error;
170
171 fn try_from(input: &'a str) -> Result<Self, Self::Error> {
172 let mut method = None;
173 let mut uri = None;
174 let mut iv = None;
175 let mut format = None;
176 let mut versions = None;
177
178 for (key, value) in AttributePairs::new(input) {
179 match key {
180 "METHOD" => method = Some(value.parse().map_err(Error::strum)?),
181 "URI" => {
182 let unquoted_uri = unquote(value);
183
184 if !unquoted_uri.trim().is_empty() {
185 uri = Some(unquoted_uri);
186 }
187 }
188 "IV" => iv = Some(value.parse()?),
189 "KEYFORMAT" => format = Some(value.into()),
190 "KEYFORMATVERSIONS" => versions = Some(value.parse()?),
191 _ => {
192 }
196 }
197 }
198
199 let method = method.ok_or_else(|| Error::missing_value("METHOD"))?;
200 let uri = uri.ok_or_else(|| Error::missing_value("URI"))?;
201 let iv = iv.unwrap_or_default();
202
203 Ok(Self {
204 method,
205 uri,
206 iv,
207 format,
208 versions,
209 })
210 }
211}
212
213impl fmt::Display for DecryptionKey<'_> {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "METHOD={},URI={}", self.method, quote(&self.uri))?;
216
217 if let InitializationVector::Aes128(_) = &self.iv {
218 write!(f, ",IV={}", &self.iv)?;
219 }
220
221 if let Some(value) = &self.format {
222 write!(f, ",KEYFORMAT={}", quote(value))?;
223 }
224
225 if let Some(value) = &self.versions {
226 if !value.is_default() {
227 write!(f, ",KEYFORMATVERSIONS={}", value)?;
228 }
229 }
230
231 Ok(())
232 }
233}
234
235impl DecryptionKeyBuilder<'_> {
236 fn validate(&self) -> Result<(), String> {
237 if self.method.is_none() {
239 return Err(Error::missing_field("DecryptionKey", "method").to_string());
240 } else if self.uri.is_none() {
241 return Err(Error::missing_field("DecryptionKey", "uri").to_string());
242 }
243
244 Ok(())
245 }
246}
247
248#[cfg(test)]
249mod test {
250 use super::*;
251 use crate::types::{EncryptionMethod, KeyFormat};
252 use pretty_assertions::assert_eq;
253
254 macro_rules! generate_tests {
255 ( $( { $struct:expr, $str:expr } ),+ $(,)* ) => {
256 #[test]
257 fn test_display() {
258 $(
259 assert_eq!($struct.to_string(), $str.to_string());
260 )+
261 }
262
263 #[test]
264 fn test_parser() {
265 $(
266 assert_eq!($struct, TryFrom::try_from($str).unwrap());
267 )+
268
269 assert_eq!(
270 DecryptionKey::new(EncryptionMethod::Aes128, "http://www.example.com"),
271 DecryptionKey::try_from(concat!(
272 "METHOD=AES-128,",
273 "URI=\"http://www.example.com\",",
274 "UNKNOWNTAG=abcd"
275 )).unwrap(),
276 );
277 assert!(DecryptionKey::try_from("METHOD=AES-128,URI=").is_err());
278 assert!(DecryptionKey::try_from("garbage").is_err());
279 }
280 }
281 }
282
283 #[test]
284 fn test_builder() {
285 let mut key = DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/");
286 key.iv = [
287 16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
288 ]
289 .into();
290 key.format = Some(KeyFormat::Identity);
291 key.versions = Some(vec![1, 2, 3, 4, 5].into());
292
293 assert_eq!(
294 DecryptionKey::builder()
295 .method(EncryptionMethod::Aes128)
296 .uri("https://www.example.com/")
297 .iv([16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82])
298 .format(KeyFormat::Identity)
299 .versions(vec![1, 2, 3, 4, 5])
300 .build()
301 .unwrap(),
302 key
303 );
304
305 assert!(DecryptionKey::builder().build().is_err());
306 assert!(DecryptionKey::builder()
307 .method(EncryptionMethod::Aes128)
308 .build()
309 .is_err());
310 }
311
312 generate_tests! {
313 {
314 DecryptionKey::new(
315 EncryptionMethod::Aes128,
316 "https://priv.example.com/key.php?r=52"
317 ),
318 concat!(
319 "METHOD=AES-128,",
320 "URI=\"https://priv.example.com/key.php?r=52\""
321 )
322 },
323 {
324 DecryptionKey::builder()
325 .method(EncryptionMethod::Aes128)
326 .uri("https://www.example.com/hls-key/key.bin")
327 .iv([16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82])
328 .build()
329 .unwrap(),
330 concat!(
331 "METHOD=AES-128,",
332 "URI=\"https://www.example.com/hls-key/key.bin\",",
333 "IV=0x10ef8f758ca555115584bb5b3c687f52"
334 )
335 },
336 {
337 DecryptionKey::builder()
338 .method(EncryptionMethod::Aes128)
339 .uri("https://www.example.com/hls-key/key.bin")
340 .iv([16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82])
341 .format(KeyFormat::Identity)
342 .versions(vec![1, 2, 3])
343 .build()
344 .unwrap(),
345 concat!(
346 "METHOD=AES-128,",
347 "URI=\"https://www.example.com/hls-key/key.bin\",",
348 "IV=0x10ef8f758ca555115584bb5b3c687f52,",
349 "KEYFORMAT=\"identity\",",
350 "KEYFORMATVERSIONS=\"1/2/3\""
351 )
352 },
353 }
354
355 #[test]
356 fn test_required_version() {
357 assert_eq!(
358 DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/")
359 .required_version(),
360 ProtocolVersion::V1
361 );
362
363 assert_eq!(
364 DecryptionKey::builder()
365 .method(EncryptionMethod::Aes128)
366 .uri("https://www.example.com/")
367 .format(KeyFormat::Identity)
368 .versions(vec![1, 2, 3])
369 .build()
370 .unwrap()
371 .required_version(),
372 ProtocolVersion::V5
373 );
374
375 assert_eq!(
376 DecryptionKey::builder()
377 .method(EncryptionMethod::Aes128)
378 .uri("https://www.example.com/")
379 .iv([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7])
380 .build()
381 .unwrap()
382 .required_version(),
383 ProtocolVersion::V2
384 );
385 }
386}