1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-05-18 16:26:07 +02:00

merge "Adding from_hex and implementing FromStr for Hash"

https://github.com/BLAKE3-team/BLAKE3/pull/24
This commit is contained in:
Jack O'Connor 2021-02-03 11:53:56 -05:00
commit 9e08f5c38d
2 changed files with 71 additions and 0 deletions

View File

@ -161,6 +161,16 @@ const KEYED_HASH: u8 = 1 << 4;
const DERIVE_KEY_CONTEXT: u8 = 1 << 5;
const DERIVE_KEY_MATERIAL: u8 = 1 << 6;
/// Errors from parsing hex values
#[derive(Debug, PartialEq)]
pub enum ParseError {
/// Hexadecimal str contains invalid character
InvalidChar,
/// Invalid str length. Only 32 byte digests can be parsed from a 64 char hex encoded str.
InvalidLen,
}
#[inline]
fn counter_low(counter: u64) -> u32 {
counter as u32
@ -232,6 +242,33 @@ impl Hash {
}
s
}
/// Parse a hexidecimal string and return the resulting Hash.
///
/// The string must be 64 characters long, producting a 32 byte digest.
/// All other string length will return a `ParseError::InvalidLen`.
pub fn from_hex(hex: &str) -> Result<Self, ParseError> {
let str_bytes = hex.as_bytes();
if str_bytes.len() != OUT_LEN * 2 {
return Err(ParseError::InvalidLen);
}
let mut bytes: [u8; OUT_LEN] = [0; OUT_LEN];
for (i, pair) in str_bytes.chunks(2).enumerate() {
bytes[i] = hex_val(pair[0])? << 4 | hex_val(pair[1])?;
}
return Ok(Hash::from(bytes));
fn hex_val(byte: u8) -> Result<u8, ParseError> {
match byte {
b'A'..=b'F' => Ok(byte - b'A' + 10),
b'a'..=b'f' => Ok(byte - b'a' + 10),
b'0'..=b'9' => Ok(byte - b'0'),
_ => Err(ParseError::InvalidChar),
}
}
}
}
impl From<[u8; OUT_LEN]> for Hash {
@ -248,6 +285,14 @@ impl From<Hash> for [u8; OUT_LEN] {
}
}
impl core::str::FromStr for Hash {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Hash::from_hex(s)
}
}
/// This implementation is constant-time.
impl PartialEq for Hash {
#[inline]

View File

@ -567,3 +567,29 @@ fn test_join_lengths() {
);
assert_eq!(CUSTOM_JOIN_CALLS.load(Ordering::SeqCst), 1);
}
#[test]
fn test_hex_encoding_decoding() {
let digest_str = "04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9";
let mut hasher = crate::Hasher::new();
hasher.update(b"foo");
let digest = hasher.finalize();
assert_eq!(digest.to_hex().as_str(), digest_str);
// Test round trip
let digest = crate::Hash::from_hex(digest_str).unwrap();
assert_eq!(digest.to_hex().as_str(), digest_str);
// Test string parsing via FromStr
let digest: crate::Hash = digest_str.parse().unwrap();
assert_eq!(digest.to_hex().as_str(), digest_str);
// Test errors
let bad_len = "04e0bb39f30b1";
let result = crate::Hash::from_hex(bad_len).unwrap_err();
assert_eq!(result, crate::ParseError::InvalidLen);
let bad_char = "Z4e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9";
let result = crate::Hash::from_hex(bad_char).unwrap_err();
assert_eq!(result, crate::ParseError::InvalidChar);
}