127 lines
3.2 KiB
Rust
127 lines
3.2 KiB
Rust
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
use std::fmt;
|
|
|
|
#[derive(Clone, Debug, PartialOrd, Ord, Deserialize, Serialize, Eq, Hash, PartialEq)]
|
|
pub struct Sha256(#[serde(serialize_with = "as_hex", deserialize_with = "from_hex")] pub [u8; 32]);
|
|
|
|
fn hash_to_hex_string(hash: &[u8; 32]) -> String {
|
|
use std::fmt::*;
|
|
|
|
let mut hex_string = String::with_capacity(64);
|
|
for digit in hash.iter() {
|
|
write!(hex_string, "{:02X}", digit).unwrap();
|
|
}
|
|
hex_string
|
|
}
|
|
|
|
impl Sha256 {
|
|
pub fn as_hex_string(&self) -> String {
|
|
hash_to_hex_string(&self.0)
|
|
}
|
|
|
|
pub fn from_data(data: &[u8]) -> Self {
|
|
use sha2::Digest;
|
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
hasher.input(data);
|
|
|
|
let hash_result = hasher.result();
|
|
|
|
let mut hash_arr = [0u8; 32];
|
|
for i in 0..32 {
|
|
hash_arr[i] = hash_result[i];
|
|
}
|
|
|
|
Sha256(hash_arr)
|
|
}
|
|
|
|
pub fn from_hex(hex_str: &str) -> Option<Self> {
|
|
if hex_str.len() != 32 * 2 {
|
|
return None; // String has wrong length
|
|
}
|
|
|
|
let mut out = [0u8; 32];
|
|
for (i, byte) in out.iter_mut().enumerate() {
|
|
let string_index = i * 2;
|
|
*byte = u8::from_str_radix(&hex_str[string_index..=string_index + 1], 16).ok()?;
|
|
}
|
|
|
|
Some(Sha256(out))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Sha256 {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
for digit in self.0.iter() {
|
|
write!(f, "{:02X}", digit)?
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn as_hex<S>(hash: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_str(&hash_to_hex_string(hash))
|
|
}
|
|
|
|
fn from_hex<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
use serde::de::Error;
|
|
|
|
String::deserialize(deserializer).and_then(|string| {
|
|
Ok(Sha256::from_hex(&string)
|
|
.ok_or_else(|| Error::custom("Invalid HEX String!".to_string()))?
|
|
.0)
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json;
|
|
|
|
const HASH_BYTES: [u8; 32] = [
|
|
0, 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,
|
|
];
|
|
|
|
const HASH_STRING: &'static str =
|
|
r#""000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F""#;
|
|
|
|
#[test]
|
|
fn serialize() {
|
|
let hash = serde_json::to_string_pretty(&Sha256(HASH_BYTES)).unwrap();
|
|
assert_eq!(&hash, HASH_STRING);
|
|
}
|
|
|
|
#[test]
|
|
fn deserialize() {
|
|
let hash: Sha256 = serde_json::from_str(HASH_STRING).unwrap();
|
|
assert_eq!(hash, Sha256(HASH_BYTES))
|
|
}
|
|
|
|
#[test]
|
|
fn fail_deserialize() {
|
|
// too short.
|
|
assert_eq!(
|
|
serde_json::from_str::<'_, Sha256>(r#""0102013""#).is_err(),
|
|
true
|
|
);
|
|
// wrong radix
|
|
assert_eq!(
|
|
serde_json::from_str::<'_, Sha256>(r#""Hallo Welt""#).is_err(),
|
|
true
|
|
);
|
|
// too long.
|
|
assert_eq!(
|
|
serde_json::from_str::<'_, Sha256>( r#""000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F""#).is_err(),
|
|
true
|
|
);
|
|
}
|
|
}
|