forked from proxmox-mirrors/proxmox
126 lines
3.1 KiB
Rust
126 lines
3.1 KiB
Rust
//! This is a general utility crate used by all our rust projects.
|
|
|
|
use failure::*;
|
|
|
|
pub mod io;
|
|
pub mod raw;
|
|
pub mod serde;
|
|
pub mod vec;
|
|
|
|
/// Evaluates to the offset (in bytes) of a given member within a struct
|
|
#[macro_export]
|
|
macro_rules! offsetof {
|
|
($ty:ty, $field:ident) => {
|
|
unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
|
|
};
|
|
}
|
|
|
|
const HEX_CHARS: &'static [u8; 16] = b"0123456789abcdef";
|
|
|
|
pub fn digest_to_hex(digest: &[u8]) -> String {
|
|
bin_to_hex(digest)
|
|
}
|
|
|
|
/// Convert a byte slice to a string of hexadecimal digits.
|
|
///
|
|
/// ```
|
|
/// # use proxmox_tools::bin_to_hex;
|
|
///
|
|
/// let text = bin_to_hex(&[1, 2, 0xff]);
|
|
/// assert_eq!(text, "0102ff");
|
|
/// ```
|
|
pub fn bin_to_hex(digest: &[u8]) -> String {
|
|
let mut buf = Vec::<u8>::with_capacity(digest.len() * 2);
|
|
|
|
for i in 0..digest.len() {
|
|
buf.push(HEX_CHARS[(digest[i] >> 4) as usize]);
|
|
buf.push(HEX_CHARS[(digest[i] & 0xf) as usize]);
|
|
}
|
|
|
|
unsafe { String::from_utf8_unchecked(buf) }
|
|
}
|
|
|
|
/// Convert a string of hexadecimal digits to a byte vector. Any non-digits are treated as an
|
|
/// error, so when there is possible whitespace in the string it must be stripped by the caller
|
|
/// first. Also, only full bytes are allowed, so the input must consist of an even number of
|
|
/// digits.
|
|
///
|
|
/// ```
|
|
/// # use proxmox_tools::hex_to_bin;
|
|
///
|
|
/// let data = hex_to_bin("aabb0123").unwrap();
|
|
/// assert_eq!(&data, &[0xaa, 0xbb, 0x01, 0x23]);
|
|
/// ```
|
|
pub fn hex_to_bin(hex: &str) -> Result<Vec<u8>, Error> {
|
|
let mut result = vec![];
|
|
|
|
let bytes = hex.as_bytes();
|
|
|
|
if (bytes.len() % 2) != 0 {
|
|
bail!("hex_to_bin: got wrong input length.");
|
|
}
|
|
|
|
let val = |c| {
|
|
if c >= b'0' && c <= b'9' {
|
|
return Ok(c - b'0');
|
|
}
|
|
if c >= b'a' && c <= b'f' {
|
|
return Ok(c - b'a' + 10);
|
|
}
|
|
if c >= b'A' && c <= b'F' {
|
|
return Ok(c - b'A' + 10);
|
|
}
|
|
bail!("found illegal hex character.");
|
|
};
|
|
|
|
for pair in bytes.chunks(2) {
|
|
let h = val(pair[0])?;
|
|
let l = val(pair[1])?;
|
|
result.push((h << 4) | l);
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
// FIXME: This should be renamed to contain the digest algorithm, so that the array's size makes
|
|
// sense.
|
|
pub fn hex_to_digest(hex: &str) -> Result<[u8; 32], Error> {
|
|
let mut digest = [0u8; 32];
|
|
|
|
let bytes = hex.as_bytes();
|
|
|
|
if bytes.len() != 64 {
|
|
bail!("got wrong digest length.");
|
|
}
|
|
|
|
let val = |c| {
|
|
if c >= b'0' && c <= b'9' {
|
|
return Ok(c - b'0');
|
|
}
|
|
if c >= b'a' && c <= b'f' {
|
|
return Ok(c - b'a' + 10);
|
|
}
|
|
if c >= b'A' && c <= b'F' {
|
|
return Ok(c - b'A' + 10);
|
|
}
|
|
bail!("found illegal hex character.");
|
|
};
|
|
|
|
let mut pos = 0;
|
|
for pair in bytes.chunks(2) {
|
|
if pos >= digest.len() {
|
|
bail!("hex digest too long.");
|
|
}
|
|
let h = val(pair[0])?;
|
|
let l = val(pair[1])?;
|
|
digest[pos] = (h << 4) | l;
|
|
pos += 1;
|
|
}
|
|
|
|
if pos != digest.len() {
|
|
bail!("hex digest too short.");
|
|
}
|
|
|
|
Ok(digest)
|
|
}
|