xmpp-rs-mirror/src/hashes.rs
Emmanuel Gil Peyrot 487dbdc6de Replace Into<Element> with From<…> for Element.
This allows Element::from() to work, and since Into<Element> is
automatically implemented for any type implementing From there is no
change to existing code.
2017-07-20 20:36:13 +01:00

142 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use try_from::TryFrom;
use std::str::FromStr;
use minidom::{Element, IntoAttributeValue};
use error::Error;
use ns;
use base64;
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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<Algo, Error> {
Ok(match s {
"" => return Err(Error::ParseError("'algo' argument cant 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 From<Algo> for String {
fn from(algo: Algo) -> String {
String::from(match algo {
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 text,
})
}
}
impl IntoAttributeValue for Algo {
fn into_attribute_value(self) -> Option<String> {
Some(String::from(self))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Hash {
pub algo: Algo,
pub hash: Vec<u8>,
}
impl TryFrom<Element> for Hash {
type Err = Error;
fn try_from(elem: Element) -> Result<Hash, Error> {
if !elem.is("hash", ns::HASHES) {
return Err(Error::ParseError("This is not a hash element."));
}
for _ in elem.children() {
return Err(Error::ParseError("Unknown child in hash element."));
}
let algo = get_attr!(elem, "algo", required);
let hash = match elem.text().as_ref() {
"" => return Err(Error::ParseError("Hash element shouldnt be empty.")),
text => base64::decode(text)?,
};
Ok(Hash {
algo: algo,
hash: hash,
})
}
}
impl From<Hash> for Element {
fn from(hash: Hash) -> Element {
Element::builder("hash")
.ns(ns::HASHES)
.attr("algo", hash.algo)
.append(base64::encode(&hash.hash))
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple() {
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=</hash>".parse().unwrap();
let hash = Hash::try_from(elem).unwrap();
assert_eq!(hash.algo, Algo::Sha_256);
assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap());
}
#[test]
fn test_unknown() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let error = Hash::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "This is not a hash element.");
}
#[test]
fn test_invalid_child() {
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>".parse().unwrap();
let error = Hash::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Unknown child in hash element.");
}
}