From b22acff15ec8a9eddd5e7a37e65d18992d06610b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 18 May 2017 23:09:29 +0100 Subject: [PATCH] hashes, ecaps2, jingle_ft: Make the algorithm a proper enum. --- src/ecaps2.rs | 113 ++++++++++++++++++++++++----------------------- src/hashes.rs | 59 +++++++++++++++++++++++-- src/jingle_ft.rs | 5 ++- 3 files changed, 116 insertions(+), 61 deletions(-) diff --git a/src/ecaps2.rs b/src/ecaps2.rs index 3d7f2c94..e397a455 100644 --- a/src/ecaps2.rs +++ b/src/ecaps2.rs @@ -8,7 +8,7 @@ use std::convert::TryFrom; use disco::{Feature, Identity, Disco}; use data_forms::DataForm; -use hashes::Hash; +use hashes::{Hash, Algo}; use minidom::Element; use error::Error; @@ -118,49 +118,52 @@ pub fn compute_disco(disco: &Disco) -> Vec { final_string } -// TODO: make algo into an enum. -pub fn hash_ecaps2(data: &[u8], algo: &str) -> String { - match algo { - "sha-256" => { - let mut hasher = Sha256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) +pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { + Ok(Hash { + algo: algo.clone(), + hash: match algo { + Algo::Sha_256 => { + let mut hasher = Sha256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha_512 => { + let mut hasher = Sha512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_256 => { + let mut hasher = Sha3_256::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Sha3_512 => { + let mut hasher = Sha3_512::default(); + hasher.input(data); + let hash = hasher.result(); + base64::encode(&hash.as_slice()) + }, + Algo::Blake2b_256 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 32] = [0; 32]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Blake2b_512 => { + let mut hasher = Blake2b::default(); + hasher.input(data); + let mut buf: [u8; 64] = [0; 64]; + let hash = hasher.variable_result(&mut buf).unwrap(); + base64::encode(hash) + }, + Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")), + Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)), }, - "sha-512" => { - let mut hasher = Sha512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-256" => { - let mut hasher = Sha3_256::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "sha3-512" => { - let mut hasher = Sha3_512::default(); - hasher.input(data); - let hash = hasher.result(); - base64::encode(&hash.as_slice()) - }, - "blake2b-256" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 32] = [0; 32]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - "blake2b-512" => { - let mut hasher = Blake2b::default(); - hasher.input(data); - let mut buf: [u8; 64] = [0; 64]; - let hash = hasher.variable_result(&mut buf).unwrap(); - base64::encode(hash) - }, - _ => panic!(), - } + }) } #[cfg(test)] @@ -174,9 +177,9 @@ mod tests { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); let ecaps2 = ECaps2::try_from(&elem).unwrap(); assert_eq!(ecaps2.hashes.len(), 2); - assert_eq!(ecaps2.hashes[0].algo, "sha-256"); + assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256); assert_eq!(ecaps2.hashes[0].hash, "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4="); - assert_eq!(ecaps2.hashes[1].algo, "sha3-256"); + assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256); assert_eq!(ecaps2.hashes[1].hash, "+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8="); } @@ -262,10 +265,10 @@ mod tests { assert_eq!(ecaps2.len(), 0x1d9); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q="); } #[test] @@ -434,15 +437,15 @@ mod tests { assert_eq!(ecaps2.len(), 0x543); assert_eq!(ecaps2, expected); - let sha_256 = ecaps2::hash_ecaps2(&ecaps2, "sha-256"); - assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); - let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, "sha3-256"); - assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); + let sha_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap(); + assert_eq!(sha_256.hash, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY="); + let sha3_256 = ecaps2::hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap(); + assert_eq!(sha3_256.hash, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg="); } #[test] fn test_blake2b_512() { - let hash = ecaps2::hash_ecaps2("abc".as_bytes(), "blake2b-512"); + let hash = ecaps2::hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap(); let known_hash: Vec = vec!( 0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, @@ -450,6 +453,6 @@ mod tests { 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23, ); let known_hash = base64::encode(&known_hash); - assert_eq!(hash, known_hash); + assert_eq!(hash.hash, known_hash); } } diff --git a/src/hashes.rs b/src/hashes.rs index 6e7bcb2d..403cb0c9 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -5,16 +5,64 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::convert::TryFrom; +use std::str::FromStr; -use minidom::Element; +use minidom::{Element, IntoAttributeValue}; use error::Error; use ns; +#[allow(non_camel_case_types)] +#[derive(Debug, Clone, PartialEq)] +pub enum Algo { + Sha_1, + Sha_256, + Sha_512, + Sha3_256, + Sha3_512, + Blake2b_256, + Blake2b_512, + Unknown(String), +} + +impl FromStr for Algo { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "" => return Err(Error::ParseError("'algo' argument can’t be empty.")), + + "sha-1" => Algo::Sha_1, + "sha-256" => Algo::Sha_256, + "sha-512" => Algo::Sha_512, + "sha3-256" => Algo::Sha3_256, + "sha3-512" => Algo::Sha3_512, + "blake2b-256" => Algo::Blake2b_256, + "blake2b-512" => Algo::Blake2b_512, + value => Algo::Unknown(value.to_owned()), + }) + } +} + +impl IntoAttributeValue for Algo { + fn into_attribute_value(self) -> Option { + Some(String::from(match self { + Algo::Sha_1 => "sha-1", + Algo::Sha_256 => "sha-256", + Algo::Sha_512 => "sha-512", + Algo::Sha3_256 => "sha3-256", + Algo::Sha3_512 => "sha3-512", + Algo::Blake2b_256 => "blake2b-256", + Algo::Blake2b_512 => "blake2b-512", + Algo::Unknown(text) => return Some(text), + })) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Hash { - pub algo: String, + pub algo: Algo, pub hash: String, } @@ -28,7 +76,10 @@ impl<'a> TryFrom<&'a Element> for Hash { for _ in elem.children() { return Err(Error::ParseError("Unknown child in hash element.")); } - let algo = elem.attr("algo").ok_or(Error::ParseError("Mandatory argument 'algo' not present in hash element."))?.to_owned(); + let algo = match elem.attr("algo") { + None => Err(Error::ParseError("Mandatory argument 'algo' not present in hash element.")), + Some(text) => Algo::from_str(text), + }?; let hash = match elem.text().as_ref() { "" => return Err(Error::ParseError("Hash element shouldn’t be empty.")), text => text.to_owned(), @@ -58,7 +109,7 @@ mod tests { fn test_simple() { let elem: Element = "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=".parse().unwrap(); let hash = Hash::try_from(&elem).unwrap(); - assert_eq!(hash.algo, "sha-256"); + assert_eq!(hash.algo, Algo::Sha_256); assert_eq!(hash.hash, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU="); } diff --git a/src/jingle_ft.rs b/src/jingle_ft.rs index efbe9c9d..9dd69df1 100644 --- a/src/jingle_ft.rs +++ b/src/jingle_ft.rs @@ -238,6 +238,7 @@ impl<'a> Into for &'a Description { #[cfg(test)] mod tests { use super::*; + use hashes::Algo; #[test] fn test_description() { @@ -261,7 +262,7 @@ mod tests { assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00"))); assert_eq!(desc.file.size, Some(6144u64)); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } @@ -283,7 +284,7 @@ mod tests { assert_eq!(desc.file.date, None); assert_eq!(desc.file.size, None); assert_eq!(desc.file.range, None); - assert_eq!(desc.file.hashes[0].algo, "sha-1"); + assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1); assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48="); } }