autoendpoint/headers/
crypto_key.rs

1use crate::headers::util::split_key_value;
2use std::collections::HashMap;
3
4/// Parses the Crypto-Key header (and similar headers) described by
5/// `http://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-00#section-4`
6pub struct CryptoKeyHeader {
7    /// The sections (comma separated) and their items (key-value semicolon separated)
8    sections: Vec<HashMap<String, String>>,
9}
10
11impl CryptoKeyHeader {
12    /// Parse a Crypto-Key header
13    pub fn parse(header: &str) -> Option<Self> {
14        let mut sections = Vec::new();
15
16        for section_str in header.split(',') {
17            let mut section = HashMap::new();
18
19            for item_str in section_str.split(';') {
20                let (key, value) = split_key_value(item_str)?;
21
22                section.insert(
23                    key.trim().to_owned(),
24                    value.trim_matches(&[' ', '"'] as &[char]).to_owned(),
25                );
26            }
27
28            sections.push(section);
29        }
30
31        Some(Self { sections })
32    }
33
34    /// Get the value of the first item with the given key
35    pub fn get_by_key(&self, key: &str) -> Option<&str> {
36        for section in &self.sections {
37            if let Some(value) = section.get(key) {
38                return Some(value.as_str());
39            }
40        }
41
42        None
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::CryptoKeyHeader;
49
50    const TEST_HEADER: &str = "keyid=\"p256dh\";dh=\"BDw9T0eImd4ax818VcYqDK_DOhcuDswKero\
51        YyNkdhYmygoLSDlSiWpuoWYUSSFxi25cyyNTR5k9Ny93DzZc0UI4\",\
52        p256ecdsa=\"BF92zdI_AKcH5Q31_Rr-04bPqOHU_Qg6lAawHbvfQrY\
53        xV_vIsAsHSyaiuyfofvxT8ZVIXccykd4V2Z7iJVfreT8\"";
54
55    #[test]
56    fn parse_succeeds() {
57        assert!(CryptoKeyHeader::parse(TEST_HEADER).is_some())
58    }
59
60    /// All items are parsed correctly
61    #[test]
62    fn parse_all_items() {
63        let crypto_keys = CryptoKeyHeader::parse(TEST_HEADER).unwrap();
64
65        assert_eq!(crypto_keys.get_by_key("keyid"), Some("p256dh"));
66        assert_eq!(
67            crypto_keys.get_by_key("dh"),
68            Some(
69                "BDw9T0eImd4ax818VcYqDK_DOhcuDswKeroYyNkdhYm\
70                 ygoLSDlSiWpuoWYUSSFxi25cyyNTR5k9Ny93DzZc0UI4"
71            )
72        );
73        assert_eq!(
74            crypto_keys.get_by_key("p256ecdsa"),
75            Some(
76                "BF92zdI_AKcH5Q31_Rr-04bPqOHU_Qg6lAawHbvfQrY\
77                 xV_vIsAsHSyaiuyfofvxT8ZVIXccykd4V2Z7iJVfreT8"
78            )
79        )
80    }
81
82    /// Accessing an unknown item returns None
83    #[test]
84    fn get_unknown() {
85        let crypto_keys = CryptoKeyHeader::parse(TEST_HEADER).unwrap();
86
87        assert!(crypto_keys.get_by_key("unknown").is_none());
88    }
89
90    /// Parsing an invalid header (no equals sign in item) returns None
91    #[test]
92    fn parse_invalid() {
93        assert!(CryptoKeyHeader::parse("key=value;invalid").is_none());
94    }
95}