1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use crate::algorithm::Algorithm;
use crate::builder::Builder;
use crate::checker::Checker;
use crate::hash::Hash;
use std::fmt;
use std::error::Error;
use hex;

#[derive(Clone, Debug)]
pub struct Integrity {
    pub hashes: Vec<Hash>
}

impl fmt::Display for Integrity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.hashes.iter()
            .map(|h| h.to_string())
            .collect::<Vec<String>>()
            .join(" "))
    }
}

impl std::str::FromStr for Integrity {
    type Err = ParseIntegrityError;

    fn from_str(s: &str) -> Result<Integrity, Self::Err> {
        let hashes = String::from(s)
            .split_whitespace()
            .map(|x| x.parse())
            .collect::<Result<Vec<Hash>, Self::Err>>()?;
        Ok(Integrity { hashes })
    }
}

impl Integrity {
    pub fn from<B: AsRef<[u8]>>(data: B, algorithm: Algorithm) -> Integrity {
        Builder::new()
            .algorithm(algorithm)
            .chain(&data)
            .result()
    }
    pub fn concat(&self, other: Integrity) -> Self {
        let mut hashes = [self.hashes.clone(), other.hashes.clone()].concat();
        hashes.sort_unstable();
        hashes.dedup();
        Integrity { hashes }
    }
    pub fn check<B: AsRef<[u8]>>(self, data: B) -> Option<Algorithm> {
        let mut checker = Checker::new(self);
        checker.input(&data);
        checker.result()
    }
    pub fn to_hex(&self) -> (Algorithm, String) {
        let hash = self.hashes.get(0).unwrap();
        (
            hash.algorithm.clone(),
            hex::encode(base64::decode(&hash.digest).unwrap())
        )
    }
}

#[derive(Debug)]
pub struct ParseIntegrityError {}
impl fmt::Display for ParseIntegrityError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "failed to parse Subresource Integrity string")
    }
}
impl Error for ParseIntegrityError {}

#[cfg(test)]
mod tests {
    use super::Hash;
    use super::Algorithm;
    use super::Integrity;

    #[test]
    fn parse() {
        let sri: Integrity = "sha1-deadbeef=".parse().unwrap();
        assert_eq!(
            sri.hashes.get(0).unwrap(),
            &Hash {
                algorithm: Algorithm::Sha1,
                digest: String::from("deadbeef=")
            }
        )
    }

    #[test]
    fn to_hex() {
        let sri = Integrity::from(b"hello world", Algorithm::Sha1);
        assert_eq!(
            sri.to_hex(),
            (
                Algorithm::Sha1,
                String::from("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")
            )
        )
    }
}