mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-09-27 14:10:05 +00:00
readded decryption_key
This commit is contained in:
parent
5486c5e830
commit
e55113e752
5 changed files with 523 additions and 715 deletions
|
@ -1,24 +1,18 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion};
|
||||||
use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion};
|
use crate::utils::tag;
|
||||||
use crate::utils::{quote, tag, unquote};
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// [4.3.4.5. EXT-X-SESSION-KEY]
|
/// [4.3.4.5. EXT-X-SESSION-KEY]
|
||||||
///
|
///
|
||||||
/// [4.3.4.5. EXT-X-SESSION-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.4.5
|
/// [4.3.4.5. EXT-X-SESSION-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.4.5
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ExtXSessionKey {
|
pub struct ExtXSessionKey(DecryptionKey);
|
||||||
method: EncryptionMethod,
|
|
||||||
uri: Option<Url>,
|
|
||||||
iv: Option<InitializationVector>,
|
|
||||||
key_format: Option<String>,
|
|
||||||
key_format_versions: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtXSessionKey {
|
impl ExtXSessionKey {
|
||||||
pub(crate) const PREFIX: &'static str = "#EXT-X-SESSION-KEY:";
|
pub(crate) const PREFIX: &'static str = "#EXT-X-SESSION-KEY:";
|
||||||
|
@ -31,266 +25,7 @@ impl ExtXSessionKey {
|
||||||
panic!("The EncryptionMethod is not allowed to be None");
|
panic!("The EncryptionMethod is not allowed to be None");
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self(DecryptionKey::new(method, uri))
|
||||||
method,
|
|
||||||
uri: Some(uri),
|
|
||||||
iv: None,
|
|
||||||
key_format: None,
|
|
||||||
key_format_versions: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [EncryptionMethod].
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.method(),
|
|
||||||
/// EncryptionMethod::Aes128
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn method(&self) -> EncryptionMethod {
|
|
||||||
self.method
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [EncryptionMethod].
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_method(EncryptionMethod::SampleAes);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES,URI=\"https://www.example.com/\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_method(&mut self, value: EncryptionMethod) -> &mut Self {
|
|
||||||
self.method = value;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `URI` that specifies how to obtain the key.
|
|
||||||
///
|
|
||||||
/// This attribute is required, if the [EncryptionMethod] is not None.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.uri(),
|
|
||||||
/// &Some("https://www.example.com".parse().unwrap())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn uri(&self) -> &Option<Url> {
|
|
||||||
&self.uri
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `URI` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is required, if the [EncryptionMethod] is not None.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_uri("http://www.google.com".parse().unwrap());
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-SESSION-KEY:METHOD=AES-128,URI=\"http://www.google.com/\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_uri(&mut self, value: Url) -> &mut Self {
|
|
||||||
self.uri = Some(value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the IV (Initialization Vector) attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_iv([
|
|
||||||
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.iv(),
|
|
||||||
/// Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7])
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn iv(&self) -> Option<[u8; 16]> {
|
|
||||||
if let Some(iv) = &self.iv {
|
|
||||||
Some(iv.to_slice())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `IV` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_iv([
|
|
||||||
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-SESSION-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",IV=0x01020304050607080901020304050607".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_iv<T>(&mut self, value: T) -> &mut Self
|
|
||||||
where
|
|
||||||
T: Into<[u8; 16]>,
|
|
||||||
{
|
|
||||||
self.iv = Some(InitializationVector(value.into()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a string that specifies how the key is
|
|
||||||
/// represented in the resource identified by the URI.
|
|
||||||
///
|
|
||||||
//// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format("key_format_attribute");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.key_format(),
|
|
||||||
/// &Some("key_format_attribute".to_string())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn key_format(&self) -> &Option<String> {
|
|
||||||
&self.key_format
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `KEYFORMAT` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format("key_format_attribute");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-SESSION-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMAT=\"key_format_attribute\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_key_format<T: ToString>(&mut self, value: T) -> &mut Self {
|
|
||||||
self.key_format = Some(value.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a string containing one or more positive
|
|
||||||
/// integers separated by the "/" character (for example, "1", "1/2",
|
|
||||||
/// or "1/2/5"). If more than one version of a particular `KEYFORMAT`
|
|
||||||
/// is defined, this attribute can be used to indicate which
|
|
||||||
/// version(s) this instance complies with.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format_versions("1/2/3/4/5");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.key_format_versions(),
|
|
||||||
/// &Some("1/2/3/4/5".to_string())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn key_format_versions(&self) -> &Option<String> {
|
|
||||||
&self.key_format_versions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `KEYFORMATVERSIONS` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXSessionKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXSessionKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format_versions("1/2/3/4/5");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-SESSION-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMATVERSIONS=\"1/2/3/4/5\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_key_format_versions<T: ToString>(&mut self, value: T) -> &mut Self {
|
|
||||||
self.key_format_versions = Some(value.to_string());
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the protocol compatibility version that this tag requires.
|
/// Returns the protocol compatibility version that this tag requires.
|
||||||
|
@ -310,9 +45,9 @@ impl ExtXSessionKey {
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn requires_version(&self) -> ProtocolVersion {
|
pub fn requires_version(&self) -> ProtocolVersion {
|
||||||
if self.key_format.is_some() | self.key_format_versions.is_some() {
|
if self.0.key_format.is_some() | self.0.key_format_versions.is_some() {
|
||||||
ProtocolVersion::V5
|
ProtocolVersion::V5
|
||||||
} else if self.iv.is_some() {
|
} else if self.0.iv.is_some() {
|
||||||
ProtocolVersion::V2
|
ProtocolVersion::V2
|
||||||
} else {
|
} else {
|
||||||
ProtocolVersion::V1
|
ProtocolVersion::V1
|
||||||
|
@ -322,20 +57,7 @@ impl ExtXSessionKey {
|
||||||
|
|
||||||
impl fmt::Display for ExtXSessionKey {
|
impl fmt::Display for ExtXSessionKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}METHOD={}", Self::PREFIX, self.method)?;
|
write!(f, "{}{}", Self::PREFIX, self.0)
|
||||||
if let Some(uri) = &self.uri {
|
|
||||||
write!(f, ",URI={}", quote(uri))?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.iv {
|
|
||||||
write!(f, ",IV={}", value)?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.key_format {
|
|
||||||
write!(f, ",KEYFORMAT={}", quote(value))?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.key_format_versions {
|
|
||||||
write!(f, ",KEYFORMATVERSIONS={}", quote(value))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,39 +66,21 @@ impl FromStr for ExtXSessionKey {
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
let input = tag(input, Self::PREFIX)?;
|
let input = tag(input, Self::PREFIX)?;
|
||||||
let mut method = None;
|
Ok(Self(input.parse()?))
|
||||||
let mut uri = None;
|
|
||||||
let mut iv = None;
|
|
||||||
let mut key_format = None;
|
|
||||||
let mut key_format_versions = None;
|
|
||||||
|
|
||||||
for (key, value) in input.parse::<AttributePairs>()? {
|
|
||||||
match key.as_str() {
|
|
||||||
"METHOD" => method = Some((value.parse())?),
|
|
||||||
"URI" => uri = Some(unquote(value).parse()?),
|
|
||||||
"IV" => iv = Some((value.parse())?),
|
|
||||||
"KEYFORMAT" => key_format = Some(unquote(value)),
|
|
||||||
"KEYFORMATVERSIONS" => key_format_versions = Some(unquote(value)),
|
|
||||||
_ => {
|
|
||||||
// [6.3.1. General Client Responsibilities]
|
|
||||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let method = method.ok_or(Error::missing_value("EXT-X-METHOD"))?;
|
impl Deref for ExtXSessionKey {
|
||||||
if method == EncryptionMethod::None {
|
type Target = DecryptionKey;
|
||||||
return Err(Error::custom(
|
|
||||||
"EXT-X-SESSION-KEY: Encryption Method must not be NONE",
|
fn deref(&self) -> &Self::Target {
|
||||||
));
|
&self.0
|
||||||
}
|
}
|
||||||
Ok(ExtXSessionKey {
|
}
|
||||||
method,
|
|
||||||
uri,
|
impl DerefMut for ExtXSessionKey {
|
||||||
iv,
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
key_format,
|
&mut self.0
|
||||||
key_format_versions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::types::{DecryptionKey, EncryptionMethod};
|
||||||
use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion};
|
use crate::utils::tag;
|
||||||
use crate::utils::{quote, tag, unquote};
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// [4.3.2.4. EXT-X-KEY]
|
/// [4.3.2.4. EXT-X-KEY]
|
||||||
|
@ -14,13 +14,7 @@ use crate::Error;
|
||||||
/// # Note
|
/// # Note
|
||||||
/// In case of an empty key (`EncryptionMethod::None`), all attributes will be ignored.
|
/// In case of an empty key (`EncryptionMethod::None`), all attributes will be ignored.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ExtXKey {
|
pub struct ExtXKey(DecryptionKey);
|
||||||
method: EncryptionMethod,
|
|
||||||
uri: Option<Url>,
|
|
||||||
iv: Option<InitializationVector>,
|
|
||||||
key_format: Option<String>,
|
|
||||||
key_format_versions: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtXKey {
|
impl ExtXKey {
|
||||||
pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:";
|
pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:";
|
||||||
|
@ -44,13 +38,7 @@ impl ExtXKey {
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn new(method: EncryptionMethod, uri: Url) -> Self {
|
pub const fn new(method: EncryptionMethod, uri: Url) -> Self {
|
||||||
Self {
|
Self(DecryptionKey::new(method, uri))
|
||||||
method,
|
|
||||||
uri: Some(uri),
|
|
||||||
iv: None,
|
|
||||||
key_format: None,
|
|
||||||
key_format_versions: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `ExtXKey` tag without a decryption key.
|
/// Makes a new `ExtXKey` tag without a decryption key.
|
||||||
|
@ -66,16 +54,16 @@ impl ExtXKey {
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn empty() -> Self {
|
pub const fn empty() -> Self {
|
||||||
Self {
|
Self(DecryptionKey {
|
||||||
method: EncryptionMethod::None,
|
method: EncryptionMethod::None,
|
||||||
uri: None,
|
uri: None,
|
||||||
iv: None,
|
iv: None,
|
||||||
key_format: None,
|
key_format: None,
|
||||||
key_format_versions: None,
|
key_format_versions: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the EncryptionMethod is None.
|
/// Returns whether the [EncryptionMethod] is [None](EncryptionMethod::None).
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
/// use hls_m3u8::tags::ExtXKey;
|
||||||
|
@ -89,313 +77,7 @@ impl ExtXKey {
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
if self.method == EncryptionMethod::None {
|
self.0.method() == EncryptionMethod::None
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [EncryptionMethod].
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.method(),
|
|
||||||
/// EncryptionMethod::Aes128
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn method(&self) -> EncryptionMethod {
|
|
||||||
self.method
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [EncryptionMethod].
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_method(EncryptionMethod::SampleAes);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-KEY:METHOD=SAMPLE-AES,URI=\"https://www.example.com/\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_method(&mut self, value: EncryptionMethod) -> &mut Self {
|
|
||||||
self.method = value;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `URI` that specifies how to obtain the key.
|
|
||||||
///
|
|
||||||
/// This attribute is required, if the [EncryptionMethod] is not None.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.uri(),
|
|
||||||
/// &Some("https://www.example.com".parse().unwrap())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn uri(&self) -> &Option<Url> {
|
|
||||||
&self.uri
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `URI` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is required, if the [EncryptionMethod] is not None.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_uri("http://www.google.com".parse().unwrap());
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"http://www.google.com/\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_uri(&mut self, value: Url) -> &mut Self {
|
|
||||||
self.uri = Some(value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the IV (Initialization Vector) attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_iv([
|
|
||||||
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.iv(),
|
|
||||||
/// Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7])
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn iv(&self) -> Option<[u8; 16]> {
|
|
||||||
if let Some(iv) = &self.iv {
|
|
||||||
Some(iv.to_slice())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `IV` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_iv([
|
|
||||||
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",IV=0x01020304050607080901020304050607".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_iv<T>(&mut self, value: T) -> &mut Self
|
|
||||||
where
|
|
||||||
T: Into<[u8; 16]>,
|
|
||||||
{
|
|
||||||
self.iv = Some(InitializationVector(value.into()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a string that specifies how the key is
|
|
||||||
/// represented in the resource identified by the URI.
|
|
||||||
///
|
|
||||||
//// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format("key_format_attribute");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.key_format(),
|
|
||||||
/// &Some("key_format_attribute".to_string())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn key_format(&self) -> &Option<String> {
|
|
||||||
&self.key_format
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `KEYFORMAT` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format("key_format_attribute");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMAT=\"key_format_attribute\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_key_format<T: ToString>(&mut self, value: T) -> &mut Self {
|
|
||||||
self.key_format = Some(value.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a string containing one or more positive
|
|
||||||
/// integers separated by the "/" character (for example, "1", "1/2",
|
|
||||||
/// or "1/2/5"). If more than one version of a particular `KEYFORMAT`
|
|
||||||
/// is defined, this attribute can be used to indicate which
|
|
||||||
/// version(s) this instance complies with.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format_versions("1/2/3/4/5");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.key_format_versions(),
|
|
||||||
/// &Some("1/2/3/4/5".to_string())
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub const fn key_format_versions(&self) -> &Option<String> {
|
|
||||||
&self.key_format_versions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `KEYFORMATVERSIONS` attribute.
|
|
||||||
///
|
|
||||||
/// This attribute is optional.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::EncryptionMethod;
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// key.set_key_format_versions("1/2/3/4/5");
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.to_string(),
|
|
||||||
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMATVERSIONS=\"1/2/3/4/5\"".to_string()
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn set_key_format_versions<T: ToString>(&mut self, value: T) -> &mut Self {
|
|
||||||
self.key_format_versions = Some(value.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the protocol compatibility version that this tag requires.
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// use hls_m3u8::tags::ExtXKey;
|
|
||||||
/// use hls_m3u8::types::{EncryptionMethod, ProtocolVersion};
|
|
||||||
///
|
|
||||||
/// let mut key = ExtXKey::new(
|
|
||||||
/// EncryptionMethod::Aes128,
|
|
||||||
/// "https://www.example.com".parse().unwrap()
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// key.requires_version(),
|
|
||||||
/// ProtocolVersion::V1
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn requires_version(&self) -> ProtocolVersion {
|
|
||||||
if self.key_format.is_some() | self.key_format_versions.is_some() {
|
|
||||||
ProtocolVersion::V5
|
|
||||||
} else if self.iv.is_some() {
|
|
||||||
ProtocolVersion::V2
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ExtXKey {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}METHOD={}", Self::PREFIX, self.method)?;
|
|
||||||
|
|
||||||
if self.method == EncryptionMethod::None {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if let Some(uri) = &self.uri {
|
|
||||||
write!(f, ",URI={}", quote(uri))?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.iv {
|
|
||||||
write!(f, ",IV={}", value)?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.key_format {
|
|
||||||
write!(f, ",KEYFORMAT={}", quote(value))?;
|
|
||||||
}
|
|
||||||
if let Some(value) = &self.key_format_versions {
|
|
||||||
write!(f, ",KEYFORMATVERSIONS={}", quote(value))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,38 +86,27 @@ impl FromStr for ExtXKey {
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
let input = tag(input, Self::PREFIX)?;
|
let input = tag(input, Self::PREFIX)?;
|
||||||
let mut method = None;
|
Ok(Self(input.parse()?))
|
||||||
let mut uri = None;
|
|
||||||
let mut iv = None;
|
|
||||||
let mut key_format = None;
|
|
||||||
let mut key_format_versions = None;
|
|
||||||
|
|
||||||
for (key, value) in input.parse::<AttributePairs>()? {
|
|
||||||
match key.as_str() {
|
|
||||||
"METHOD" => method = Some((value.parse())?),
|
|
||||||
"URI" => uri = Some(unquote(value).parse()?),
|
|
||||||
"IV" => iv = Some((value.parse())?),
|
|
||||||
"KEYFORMAT" => key_format = Some(unquote(value)),
|
|
||||||
"KEYFORMATVERSIONS" => key_format_versions = Some(unquote(value)),
|
|
||||||
_ => {
|
|
||||||
// [6.3.1. General Client Responsibilities]
|
|
||||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let method = method.ok_or(Error::missing_value("EXT-X-KEY:METHOD"))?;
|
impl fmt::Display for ExtXKey {
|
||||||
if method != EncryptionMethod::None && uri.is_none() {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
return Err(Error::missing_value("EXT-X-KEY:URI"));
|
write!(f, "{}{}", Self::PREFIX, self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExtXKey {
|
impl Deref for ExtXKey {
|
||||||
method,
|
type Target = DecryptionKey;
|
||||||
uri,
|
|
||||||
iv,
|
fn deref(&self) -> &Self::Target {
|
||||||
key_format,
|
&self.0
|
||||||
key_format_versions,
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ExtXKey {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,26 +122,16 @@ mod test {
|
||||||
"#EXT-X-KEY:METHOD=NONE".to_string()
|
"#EXT-X-KEY:METHOD=NONE".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
let mut key = ExtXKey::empty();
|
||||||
ExtXKey::empty().set_key_format("hi").to_string(),
|
// it is expected, that all attributes will be ignored in an empty key!
|
||||||
"#EXT-X-KEY:METHOD=NONE".to_string()
|
key.set_key_format("hi");
|
||||||
);
|
|
||||||
|
|
||||||
let mut key = ExtXKey::new(
|
|
||||||
EncryptionMethod::Aes128,
|
|
||||||
"https://www.example.com/hls-key/key.bin".parse().unwrap(),
|
|
||||||
);
|
|
||||||
key.set_iv([
|
key.set_iv([
|
||||||
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
]);
|
]);
|
||||||
|
key.set_uri("https://www.example.com".parse().unwrap());
|
||||||
|
key.set_key_format_versions("1/2/3");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(key.to_string(), "#EXT-X-KEY:METHOD=NONE".to_string());
|
||||||
key.to_string(),
|
|
||||||
"#EXT-X-KEY:METHOD=AES-128,\
|
|
||||||
URI=\"https://www.example.com/hls-key/key.bin\",\
|
|
||||||
IV=0x10ef8f758ca555115584bb5b3c687f52"
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -492,29 +153,5 @@ mod test {
|
||||||
key.set_iv([
|
key.set_iv([
|
||||||
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
"#EXT-X-KEY:METHOD=AES-128,\
|
|
||||||
URI=\"https://www.example.com/hls-key/key.bin\",\
|
|
||||||
IV=0X10ef8f758ca555115584bb5b3c687f52"
|
|
||||||
.parse::<ExtXKey>()
|
|
||||||
.unwrap(),
|
|
||||||
key
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut key = ExtXKey::new(
|
|
||||||
EncryptionMethod::Aes128,
|
|
||||||
"http://www.example.com".parse().unwrap(),
|
|
||||||
);
|
|
||||||
key.set_iv([
|
|
||||||
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
|
||||||
])
|
|
||||||
.set_key_format("baz");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
r#"#EXT-X-KEY:METHOD=AES-128,URI="http://www.example.com",IV=0x10ef8f758ca555115584bb5b3c687f52,KEYFORMAT="baz""#
|
|
||||||
.parse::<ExtXKey>().unwrap(),
|
|
||||||
key
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
465
src/types/decryption_key.rs
Normal file
465
src/types/decryption_key.rs
Normal file
|
@ -0,0 +1,465 @@
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use derive_builder::Builder;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::attribute::AttributePairs;
|
||||||
|
use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion};
|
||||||
|
use crate::utils::{quote, unquote};
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub struct DecryptionKey {
|
||||||
|
pub(crate) method: EncryptionMethod,
|
||||||
|
#[builder(setter(into, strip_option), default)]
|
||||||
|
pub(crate) uri: Option<Url>,
|
||||||
|
#[builder(setter(into, strip_option), default)]
|
||||||
|
pub(crate) iv: Option<InitializationVector>,
|
||||||
|
#[builder(setter(into, strip_option), default)]
|
||||||
|
pub(crate) key_format: Option<String>,
|
||||||
|
#[builder(setter(into, strip_option), default)]
|
||||||
|
pub(crate) key_format_versions: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecryptionKey {
|
||||||
|
/// Makes a new `DecryptionKey`.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use url::Url;
|
||||||
|
///
|
||||||
|
/// use hls_m3u8::types::{EncryptionMethod, DecryptionKey};
|
||||||
|
///
|
||||||
|
/// let key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=AES-128,URI=\"https://www.example.com/\""
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub const fn new(method: EncryptionMethod, uri: Url) -> Self {
|
||||||
|
Self {
|
||||||
|
method,
|
||||||
|
uri: Some(uri),
|
||||||
|
iv: None,
|
||||||
|
key_format: None,
|
||||||
|
key_format_versions: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [EncryptionMethod].
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.method(),
|
||||||
|
/// EncryptionMethod::Aes128
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub const fn method(&self) -> EncryptionMethod {
|
||||||
|
self.method
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a Builder to build a `DecryptionKey`.
|
||||||
|
pub fn builder() -> DecryptionKeyBuilder {
|
||||||
|
DecryptionKeyBuilder::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the [EncryptionMethod].
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_method(EncryptionMethod::SampleAes);
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=SAMPLE-AES,URI=\"https://www.example.com/\"".to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn set_method(&mut self, value: EncryptionMethod) {
|
||||||
|
self.method = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an `URI` that specifies how to obtain the key.
|
||||||
|
///
|
||||||
|
/// This attribute is required, if the [EncryptionMethod] is not None.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.uri(),
|
||||||
|
/// &Some("https://www.example.com".parse().unwrap())
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub const fn uri(&self) -> &Option<Url> {
|
||||||
|
&self.uri
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `URI` attribute.
|
||||||
|
///
|
||||||
|
/// This attribute is required, if the [EncryptionMethod] is not None.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_uri("http://www.google.com".parse().unwrap());
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=AES-128,URI=\"http://www.google.com/\"".to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn set_uri(&mut self, value: Url) {
|
||||||
|
self.uri = Some(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the IV (Initialization Vector) attribute.
|
||||||
|
///
|
||||||
|
/// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_iv([
|
||||||
|
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.iv(),
|
||||||
|
/// Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7])
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn iv(&self) -> Option<[u8; 16]> {
|
||||||
|
if let Some(iv) = &self.iv {
|
||||||
|
Some(iv.to_slice())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `IV` attribute.
|
||||||
|
///
|
||||||
|
/// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_iv([
|
||||||
|
/// 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=AES-128,URI=\"https://www.example.com/\",IV=0x01020304050607080901020304050607".to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn set_iv<T>(&mut self, value: T)
|
||||||
|
where
|
||||||
|
T: Into<[u8; 16]>,
|
||||||
|
{
|
||||||
|
self.iv = Some(InitializationVector(value.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a string that specifies how the key is
|
||||||
|
/// represented in the resource identified by the URI.
|
||||||
|
///
|
||||||
|
//// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_key_format("key_format_attribute");
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.key_format(),
|
||||||
|
/// &Some("key_format_attribute".to_string())
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub const fn key_format(&self) -> &Option<String> {
|
||||||
|
&self.key_format
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `KEYFORMAT` attribute.
|
||||||
|
///
|
||||||
|
/// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_key_format("key_format_attribute");
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMAT=\"key_format_attribute\"".to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn set_key_format<T: ToString>(&mut self, value: T) {
|
||||||
|
self.key_format = Some(value.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a string containing one or more positive
|
||||||
|
/// integers separated by the "/" character (for example, "1", "1/2",
|
||||||
|
/// or "1/2/5"). If more than one version of a particular `KEYFORMAT`
|
||||||
|
/// is defined, this attribute can be used to indicate which
|
||||||
|
/// version(s) this instance complies with.
|
||||||
|
///
|
||||||
|
/// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_key_format_versions("1/2/3/4/5");
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.key_format_versions(),
|
||||||
|
/// &Some("1/2/3/4/5".to_string())
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub const fn key_format_versions(&self) -> &Option<String> {
|
||||||
|
&self.key_format_versions
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `KEYFORMATVERSIONS` attribute.
|
||||||
|
///
|
||||||
|
/// This attribute is optional.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{DecryptionKey, EncryptionMethod};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// key.set_key_format_versions("1/2/3/4/5");
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.to_string(),
|
||||||
|
/// "METHOD=AES-128,URI=\"https://www.example.com/\",KEYFORMATVERSIONS=\"1/2/3/4/5\"".to_string()
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn set_key_format_versions<T: ToString>(&mut self, value: T) {
|
||||||
|
self.key_format_versions = Some(value.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the protocol compatibility version that this tag requires.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::types::{EncryptionMethod, ProtocolVersion, DecryptionKey};
|
||||||
|
///
|
||||||
|
/// let mut key = DecryptionKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "https://www.example.com".parse().unwrap()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// key.requires_version(),
|
||||||
|
/// ProtocolVersion::V1
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn requires_version(&self) -> ProtocolVersion {
|
||||||
|
if self.key_format.is_some() || self.key_format_versions.is_some() {
|
||||||
|
ProtocolVersion::V5
|
||||||
|
} else if self.iv.is_some() {
|
||||||
|
ProtocolVersion::V2
|
||||||
|
} else {
|
||||||
|
ProtocolVersion::V1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DecryptionKey {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut method = None;
|
||||||
|
let mut uri = None;
|
||||||
|
let mut iv = None;
|
||||||
|
let mut key_format = None;
|
||||||
|
let mut key_format_versions = None;
|
||||||
|
|
||||||
|
for (key, value) in input.parse::<AttributePairs>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"METHOD" => method = Some((value.parse())?),
|
||||||
|
"URI" => uri = Some(unquote(value).parse()?),
|
||||||
|
"IV" => iv = Some((value.parse())?),
|
||||||
|
"KEYFORMAT" => key_format = Some(unquote(value)),
|
||||||
|
"KEYFORMATVERSIONS" => key_format_versions = Some(unquote(value)),
|
||||||
|
_ => {
|
||||||
|
// [6.3.1. General Client Responsibilities]
|
||||||
|
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let method = method.ok_or(Error::missing_value("METHOD"))?;
|
||||||
|
if method != EncryptionMethod::None && uri.is_none() {
|
||||||
|
return Err(Error::missing_value("URI"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DecryptionKey {
|
||||||
|
method,
|
||||||
|
uri,
|
||||||
|
iv,
|
||||||
|
key_format,
|
||||||
|
key_format_versions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DecryptionKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "METHOD={}", self.method)?;
|
||||||
|
|
||||||
|
if self.method == EncryptionMethod::None {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(uri) = &self.uri {
|
||||||
|
write!(f, ",URI={}", quote(uri))?;
|
||||||
|
}
|
||||||
|
if let Some(value) = &self.iv {
|
||||||
|
write!(f, ",IV={}", value)?;
|
||||||
|
}
|
||||||
|
if let Some(value) = &self.key_format {
|
||||||
|
write!(f, ",KEYFORMAT={}", quote(value))?;
|
||||||
|
}
|
||||||
|
if let Some(value) = &self.key_format_versions {
|
||||||
|
write!(f, ",KEYFORMATVERSIONS={}", quote(value))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::types::EncryptionMethod;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_requires_version() {
|
||||||
|
let key = DecryptionKey::builder()
|
||||||
|
.method(EncryptionMethod::Aes128)
|
||||||
|
.uri("https://www.example.com".parse::<Url>().unwrap())
|
||||||
|
.iv([
|
||||||
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
|
])
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_display() {
|
||||||
|
let mut key = DecryptionKey::new(
|
||||||
|
EncryptionMethod::Aes128,
|
||||||
|
"https://www.example.com/hls-key/key.bin".parse().unwrap(),
|
||||||
|
);
|
||||||
|
key.set_iv([
|
||||||
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
key.to_string(),
|
||||||
|
"METHOD=AES-128,\
|
||||||
|
URI=\"https://www.example.com/hls-key/key.bin\",\
|
||||||
|
IV=0x10ef8f758ca555115584bb5b3c687f52"
|
||||||
|
.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser() {
|
||||||
|
assert_eq!(
|
||||||
|
r#"METHOD=AES-128,URI="https://priv.example.com/key.php?r=52""#
|
||||||
|
.parse::<DecryptionKey>()
|
||||||
|
.unwrap(),
|
||||||
|
DecryptionKey::new(
|
||||||
|
EncryptionMethod::Aes128,
|
||||||
|
"https://priv.example.com/key.php?r=52".parse().unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut key = DecryptionKey::new(
|
||||||
|
EncryptionMethod::Aes128,
|
||||||
|
"https://www.example.com/hls-key/key.bin".parse().unwrap(),
|
||||||
|
);
|
||||||
|
key.set_iv([
|
||||||
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"METHOD=AES-128,\
|
||||||
|
URI=\"https://www.example.com/hls-key/key.bin\",\
|
||||||
|
IV=0X10ef8f758ca555115584bb5b3c687f52"
|
||||||
|
.parse::<DecryptionKey>()
|
||||||
|
.unwrap(),
|
||||||
|
key
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut key = DecryptionKey::new(
|
||||||
|
EncryptionMethod::Aes128,
|
||||||
|
"http://www.example.com".parse().unwrap(),
|
||||||
|
);
|
||||||
|
key.set_iv([
|
||||||
|
16, 239, 143, 117, 140, 165, 85, 17, 85, 132, 187, 91, 60, 104, 127, 82,
|
||||||
|
]);
|
||||||
|
key.set_key_format("baz");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
r#"METHOD=AES-128,URI="http://www.example.com",IV=0x10ef8f758ca555115584bb5b3c687f52,KEYFORMAT="baz""#
|
||||||
|
.parse::<DecryptionKey>().unwrap(),
|
||||||
|
key
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ use crate::Error;
|
||||||
///
|
///
|
||||||
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
|
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub(crate) struct InitializationVector(pub [u8; 16]);
|
pub struct InitializationVector(pub [u8; 16]);
|
||||||
|
|
||||||
impl InitializationVector {
|
impl InitializationVector {
|
||||||
pub const fn to_slice(&self) -> [u8; 16] {
|
pub const fn to_slice(&self) -> [u8; 16] {
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod byte_range;
|
||||||
mod closed_captions;
|
mod closed_captions;
|
||||||
mod decimal_floating_point;
|
mod decimal_floating_point;
|
||||||
mod decimal_resolution;
|
mod decimal_resolution;
|
||||||
|
mod decryption_key;
|
||||||
mod encryption_method;
|
mod encryption_method;
|
||||||
mod hdcp_level;
|
mod hdcp_level;
|
||||||
mod in_stream_id;
|
mod in_stream_id;
|
||||||
|
@ -15,10 +16,11 @@ pub use byte_range::*;
|
||||||
pub use closed_captions::*;
|
pub use closed_captions::*;
|
||||||
pub(crate) use decimal_floating_point::*;
|
pub(crate) use decimal_floating_point::*;
|
||||||
pub(crate) use decimal_resolution::*;
|
pub(crate) use decimal_resolution::*;
|
||||||
|
pub use decryption_key::*;
|
||||||
pub use encryption_method::*;
|
pub use encryption_method::*;
|
||||||
pub use hdcp_level::*;
|
pub use hdcp_level::*;
|
||||||
pub use in_stream_id::*;
|
pub use in_stream_id::*;
|
||||||
pub(crate) use initialization_vector::*;
|
pub use initialization_vector::*;
|
||||||
pub use media_type::*;
|
pub use media_type::*;
|
||||||
pub use protocol_version::*;
|
pub use protocol_version::*;
|
||||||
pub(crate) use signed_decimal_floating_point::*;
|
pub(crate) use signed_decimal_floating_point::*;
|
||||||
|
|
Loading…
Reference in a new issue