Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/hawk/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 14 kB image not shown  

Quelle  header.rs

  Sprache: Rust
 

use crate::b64;
use crate::error::*;
use crate::mac::Mac;
use base64::Engine;
use std::fmt;
use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

/// Representation of a Hawk `Authorization` header value (the part following "Hawk ").
///
/// Headers can be derived from strings using the `FromStr` trait, and formatted into a
/// string using the `fmt_header` method.
///
/// All fields are optional, although for specific purposes some fields must be present.
#[derive(Clone, PartialEq, Debug)]
pub struct Header {
    pub id: Option<String>,
    pub ts: Option<SystemTime>,
    pub nonce: Option<String>,
    pub mac: Option<Mac>,
    pub ext: Option<String>,
    pub hash: Option<Vec<u8>>,
    pub app: Option<String>,
    pub dlg: Option<String>,
}

impl Header {
    /// Create a new Header with the full set of Hawk fields.
    ///
    /// This is a low-level function. Headers are more often created from Requests or Responses.
    ///
    /// Note that none of the string-formatted header components can contain the character `\"`.
    pub fn new<S>(
        id: Option<S>,
        ts: Option<SystemTime>,
        nonce: Option<S>,
        mac: Option<Mac>,
        ext: Option<S>,
        hash: Option<Vec<u8>>,
        app: Option<S>,
        dlg: Option<S>,
    ) -> Result<Header>
    where
        S: Into<String>,
    {
        Ok(Header {
            id: Header::check_component(id)?,
            ts,
            nonce: Header::check_component(nonce)?,
            mac,
            ext: Header::check_component(ext)?,
            hash,
            app: Header::check_component(app)?,
            dlg: Header::check_component(dlg)?,
        })
    }

    /// Check a header component for validity.
    fn check_component<S>(value: Option<S>) -> Result<Option<String>>
    where
        S: Into<String>,
    {
        if let Some(value) = value {
            let value = value.into();
            if value.contains('\"') {
                return Err(Error::HeaderParseError(
                    "Hawk headers cannot contain `\\`".into(),
                ));
            }
            Ok(Some(value))
        } else {
            Ok(None)
        }
    }

    /// Format the header for transmission in an Authorization header, omitting the `"Hawk "`
    /// prefix.
    pub fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut sep = "";
        if let Some(ref id) = self.id {
            write!(f, "{sep}id=\"{id}\"")?;
            sep = ", ";
        }
        if let Some(ref ts) = self.ts {
            write!(
                f,
                "{}ts=\"{}\"",
                sep,
                ts.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()
            )?;
            sep = ", ";
        }
        if let Some(ref nonce) = self.nonce {
            write!(f, "{sep}nonce=\"{nonce}\"")?;
            sep = ", ";
        }
        if let Some(ref mac) = self.mac {
            write!(f, "{}mac=\"{}\"", sep, b64::STANDARD_ENGINE.encode(mac))?;
            sep = ", ";
        }
        if let Some(ref ext) = self.ext {
            write!(f, "{sep}ext=\"{ext}\"")?;
            sep = ", ";
        }
        if let Some(ref hash) = self.hash {
            write!(f, "{}hash=\"{}\"", sep, b64::STANDARD_ENGINE.encode(hash))?;
            sep = ", ";
        }
        if let Some(ref app) = self.app {
            write!(f, "{sep}app=\"{app}\"")?;
            sep = ", ";
        }
        if let Some(ref dlg) = self.dlg {
            write!(f, "{sep}dlg=\"{dlg}\"")?;
        }
        Ok(())
    }
}

impl fmt::Display for Header {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.fmt_header(f)
    }
}

impl FromStr for Header {
    type Err = Error;
    fn from_str(s: &str) -> Result<Header> {
        let mut p = s;

        // Required attributes
        let mut id: Option<&str> = None;
        let mut ts: Option<SystemTime> = None;
        let mut nonce: Option<&str> = None;
        let mut mac: Option<Vec<u8>> = None;
        // Optional attributes
        let mut hash: Option<Vec<u8>> = None;
        let mut ext: Option<&str> = None;
        let mut app: Option<&str> = None;
        let mut dlg: Option<&str> = None;

        while !p.is_empty() {
            // Skip whitespace and commas used as separators
            p = p.trim_start_matches(|c| c == ',' || char::is_whitespace(c));
            // Find first '=' which delimits attribute name from value
            let assign_end = p
                .find('=')
                .ok_or_else(|| Error::HeaderParseError("Expected '='".into()))?;
            let attr = &p[..assign_end].trim();
            if p.len() < assign_end + 1 {
                return Err(Error::HeaderParseError(
                    "Missing right hand side of =".into(),
                ));
            }
            p = p[assign_end + 1..].trim_start();
            if !p.starts_with('\"') {
                return Err(Error::HeaderParseError("Expected opening quote".into()));
            }
            p = &p[1..];
            // We have poor RFC 7235 compliance here as we ought to support backslash
            // escaped characters, but hawk doesn't allow this we won't either.  All
            // strings must be surrounded by ".." and contain no such characters.
            let end = p.find('\"');
            let val_end =
                end.ok_or_else(|| Error::HeaderParseError("Expected closing quote".into()))?;
            let val = &p[..val_end];
            match *attr {
                "id" => id = Some(val),
                "ts" => {
                    let epoch = u64::from_str(val)
                        .map_err(|_| Error::HeaderParseError("Error parsing `ts` field".into()))?;
                    ts = Some(UNIX_EPOCH + Duration::new(epoch, 0));
                }
                "mac" => {
                    mac = Some(b64::STANDARD_ENGINE.decode(val).map_err(|_| {
                        Error::HeaderParseError("Error parsing `mac` field".into())
                    })?);
                }
                "nonce" => nonce = Some(val),
                "ext" => ext = Some(val),
                "hash" => {
                    hash = Some(b64::STANDARD_ENGINE.decode(val).map_err(|_| {
                        Error::HeaderParseError("Error parsing `hash` field".into())
                    })?);
                }
                "app" => app = Some(val),
                "dlg" => dlg = Some(val),
                _ => {
                    return Err(Error::HeaderParseError(format!(
                        "Invalid Hawk field {}",
                        *attr
                    )))
                }
            };
            // Break if we are at end of string, otherwise skip separator
            if p.len() < val_end + 1 {
                break;
            }
            p = p[val_end + 1..].trim_start();
        }

        Ok(Header {
            id: id.map(|id| id.to_string()),
            ts,
            nonce: nonce.map(|nonce| nonce.to_string()),
            mac: mac.map(Mac::from),
            ext: ext.map(|ext| ext.to_string()),
            hash,
            app: app.map(|app| app.to_string()),
            dlg: dlg.map(|dlg| dlg.to_string()),
        })
    }
}

#[cfg(test)]
mod test {
    use super::Header;
    use crate::mac::Mac;
    use std::str::FromStr;
    use std::time::{Duration, UNIX_EPOCH};

    #[test]
    fn illegal_id() {
        assert!(Header::new(
            Some("ab\"cdef"),
            Some(UNIX_EPOCH + Duration::new(12340)),
            Some("nonce"),
            Some(Mac::from(vec![])),
            Some("ext"),
            None,
            None,
            None
        )
        .is_err());
    }

    #[test]
    fn illegal_nonce() {
        assert!(Header::new(
            Some("abcdef"),
            Some(UNIX_EPOCH + Duration::new(12340)),
            Some("no\"nce"),
            Some(Mac::from(vec![])),
            Some("ext"),
            None,
            None,
            None
        )
        .is_err());
    }

    #[test]
    fn illegal_ext() {
        assert!(Header::new(
            Some("abcdef"),
            Some(UNIX_EPOCH + Duration::new(12340)),
            Some("nonce"),
            Some(Mac::from(vec![])),
            Some("ex\"t"),
            None,
            None,
            None
        )
        .is_err());
    }

    #[test]
    fn illegal_app() {
        assert!(Header::new(
            Some("abcdef"),
            Some(UNIX_EPOCH + Duration::new(12340)),
            Some("nonce"),
            Some(Mac::from(vec![])),
            None,
            None,
            Some("a\"pp"),
            None
        )
        .is_err());
    }

    #[test]
    fn illegal_dlg() {
        assert!(Header::new(
            Some("abcdef"),
            Some(UNIX_EPOCH + Duration::new(12340)),
            Some("nonce"),
            Some(Mac::from(vec![])),
            None,
            None,
            None,
            Some("d\"lg")
        )
        .is_err());
    }

    #[test]
    fn from_str() {
        let s = Header::from_str(
            "id=\"dh37fgj492je\", ts=\"1353832234\", \
             nonce=\"j4h3g2\", ext=\"some-app-ext-data\", \
             mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
             hash=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
             app=\"my-app\", dlg=\"my-authority\"",
        )
        .unwrap();
        assert!(s.id == Some("dh37fgj492je".to_string()));
        assert!(s.ts == Some(UNIX_EPOCH + Duration::new(13538322340)));
        assert!(s.nonce == Some("j4h3g2".to_string()));
        assert!(
            s.mac
                == Some(Mac::from(vec![
                    2333043871521322482112322021111501945513520648,
                    6937575521401021639123350135233441
                ]))
        );
        assert!(s.ext == Some("some-app-ext-data".to_string()));
        assert!(s.app == Some("my-app".to_string()));
        assert!(s.dlg == Some("my-authority".to_string()));
    }

    #[test]
    fn from_str_invalid_mac() {
        let r = Header::from_str(
            "id=\"dh37fgj492je\", ts=\"1353832234\", \
             nonce=\"j4h3g2\", ext=\"some-app-ext-data\", \
             mac=\"6!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!AE=\", \
             app=\"my-app\", dlg=\"my-authority\"",
        );
        assert!(r.is_err());
    }

    #[test]
    fn from_str_no_field() {
        let s = Header::from_str("").unwrap();
        assert!(s.id.is_none());
        assert!(s.ts.is_none());
        assert!(s.nonce.is_none());
        assert!(s.mac.is_none());
        assert!(s.ext.is_none());
        assert!(s.app.is_none());
        assert!(s.dlg.is_none());
    }

    #[test]
    fn from_str_few_field() {
        let s = Header::from_str(
            "id=\"xyz\", ts=\"1353832234\", \
             nonce=\"abc\", \
             mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"",
        )
        .unwrap();
        assert!(s.id == Some("xyz".to_string()));
        assert!(s.ts == Some(UNIX_EPOCH + Duration::new(13538322340)));
        assert!(s.nonce == Some("abc".to_string()));
        assert!(
            s.mac
                == Some(Mac::from(vec![
                    2333043871521322482112322021111501945513520648,
                    6937575521401021639123350135233441
                ]))
        );
        assert!(s.ext.is_none());
        assert!(s.app.is_none());
        assert!(s.dlg.is_none());
    }

    #[test]
    fn from_str_messy() {
        let s = Header::from_str(
            ", id  =  \"dh37fgj492je\", ts=\"1353832234\", \
             nonce=\"j4h3g2\"  , , ext=\"some-app-ext-data\", \
             mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"",
        )
        .unwrap();
        assert!(s.id == Some("dh37fgj492je".to_string()));
        assert!(s.ts == Some(UNIX_EPOCH + Duration::new(13538322340)));
        assert!(s.nonce == Some("j4h3g2".to_string()));
        assert!(
            s.mac
                == Some(Mac::from(vec![
                    2333043871521322482112322021111501945513520648,
                    6937575521401021639123350135233441
                ]))
        );
        assert!(s.ext == Some("some-app-ext-data".to_string()));
        assert!(s.app.is_none());
        assert!(s.dlg.is_none());
    }

    #[test]
    fn to_str_no_fields() {
        // must supply a type for S, since it is otherwise unused
        let s = Header::new::<String>(None, None, None, None, None, None, None, None).unwrap();
        let formatted = format!("{s}");
        println!("got: {formatted}");
        assert!(formatted.is_empty())
    }

    #[test]
    fn to_str_few_fields() {
        let s = Header::new(
            Some("dh37fgj492je"),
            Some(UNIX_EPOCH + Duration::new(13538322340)),
            Some("j4h3g2"),
            Some(Mac::from(vec![
                8351821494211133192192294431181766569864156,
                184851072492421722006620957633883,
            ])),
            None,
            None,
            None,
            None,
        )
        .unwrap();
        let formatted = format!("{s}");
        println!("got: {formatted}");
        assert!(
            formatted
                == "id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", \
                    mac=\"CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=\""
        )
    }

    #[test]
    fn to_str_maximal() {
        let s = Header::new(
            Some("dh37fgj492je"),
            Some(UNIX_EPOCH + Duration::new(13538322340)),
            Some("j4h3g2"),
            Some(Mac::from(vec![
                8351821494211133192192294431181766569864156,
                184851072492421722006620957633883,
            ])),
            Some("my-ext-value"),
            Some(vec![1234]),
            Some("my-app"),
            Some("my-dlg"),
        )
        .unwrap();
        let formatted = format!("{s}");
        println!("got: {formatted}");
        assert!(
            formatted
                == "id=\"dh37fgj492je\", ts=\"1353832234\", nonce=\"j4h3g2\", \
                    mac=\"CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=\", ext=\"my-ext-value\", \
                    hash=\"AQIDBA==\", app=\"my-app\", dlg=\"my-dlg\""
        )
    }

    #[test]
    fn round_trip() {
        let s = Header::new(
            Some("dh37fgj492je"),
            Some(UNIX_EPOCH + Duration::new(13538322340)),
            Some("j4h3g2"),
            Some(Mac::from(vec![
                8351821494211133192192294431181766569864156,
                184851072492421722006620957633883,
            ])),
            Some("my-ext-value"),
            Some(vec![1234]),
            Some("my-app"),
            Some("my-dlg"),
        )
        .unwrap();
        let formatted = format!("{s}");
        println!("got: {s}");
        let s2 = Header::from_str(&formatted).unwrap();
        assert!(s2 == s);
    }
}

Messung V0.5 in Prozent
C=78 H=95 G=86

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.