commit 5a2c84cb6810f8bfff2305e13ea020821f6e4847 Author: Avril Date: Mon Aug 24 05:25:17 2020 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80aca69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +*~ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b9b802e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "cryptohelpers" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +openssl = {version = "0.10", optional = true} +pbkdf2 = {version = "0.5", optional = true } +sha2 = {version = "0.9", optional = true } +hmac = {version = "0.9", optional = true } +getrandom = {version = "0.1", optional = true } +crc = {version = "1.8", optional = true } +hex-literal = {version = "0.3", optional = true } +libc = "0.2" +tokio = {version = "0.2", features=["io-util"], optional=true} + +[features] +default=["full", "async"] + +async = ["tokio"] + +# Actual things +full = [ + "sha256", + "password", + "aes", + "checksum" +] + +sha256 = ["sha2"] +password = ["sha256", "pbkdf2", "hex-literal", "hmac", "getrandom"] +aes = ["openssl", "getrandom"] +checksum = ["crc"] + +[build-dependencies] +rustc_version = "0.2" diff --git a/TODO b/TODO new file mode 100644 index 0000000..e036bc8 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +RSA diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/aes.rs b/src/aes.rs new file mode 100644 index 0000000..b492d88 --- /dev/null +++ b/src/aes.rs @@ -0,0 +1,226 @@ +//! Advanced Encryption Standard +use super::*; +#[allow(unused_imports)] +use std::{ + fmt, + marker::Unpin, + io, +}; +use openssl::{ + symm::{ + Cipher, + Crypter, + Mode, + }, +}; +#[cfg(feature="async")] +use tokio::{ + io::{ + AsyncRead, + AsyncWrite, + }, + prelude::*, +}; +use getrandom::getrandom; + +const KEYSIZE: usize = consts::AES_KEYSIZE; +const IVSIZE: usize = consts::AES_IVSIZE; +use consts::BUFFER_SIZE; +const BLOCKSIZE: usize = 16; + +/// A key and IV for the AES algorithm +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)] +#[repr(C, packed)] +pub struct AesKey { + key: [u8; KEYSIZE], + iv: [u8; IVSIZE], +} + +impl AesKey +{ + /// Generate a new random AES key and IV. + pub fn random() -> Result + { + let mut this = Self::default(); + + getrandom(&mut this.key[..])?; + getrandom(&mut this.iv[..])?; + + Ok(this) + } + + /// Create a new instance from a key and IV + pub const fn new(key: [u8; KEYSIZE], iv: [u8; IVSIZE]) -> Self + { + Self{key,iv} + } + + /// Create a new instance from slices + pub fn from_slice(key: impl AsRef<[u8]>, iv: impl AsRef<[u8]>) -> Result + { + let mut this = Self::default(); + if bytes::copy_slice(&mut this.key[..], key.as_ref()) != this.key.len() { + Err(Error::Length{expected: Some(this.key.len()), got: None}) + } else { + Ok(()) + }?; + + if bytes::copy_slice(&mut this.iv[..], iv.as_ref()) != this.iv.len() { + Err(Error::Length{expected: Some(this.iv.len()), got: None}) + } else { + Ok(this) + } + } + + /// The key part of this `AesKey` instance + pub fn k(&self) -> &[u8] + { + &self.key[..] + } + + /// The IV part of this `AesKey` instance + pub fn i(&self) -> &[u8] + { + &self.iv[..] + } + + /// A mutable reference of the key part of this `AesKey` instance + pub fn k_mut(&mut self) -> &mut [u8] + { + &mut self.key[..] + } + + /// A mutable reference of the IV part of this `AesKey` instance + pub fn i_mut(&mut self) -> &mut [u8] + { + &mut self.iv[..] + } +} + +impl AsRef<[u8]> for AesKey +{ + fn as_ref(&self) -> &[u8] + { + bytes::refer(self) + } +} + +impl AsMut<[u8]> for AesKey +{ + fn as_mut(&mut self) -> &mut [u8] + { + bytes::refer_mut(self) + } +} + +impl fmt::Display for AesKey +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "AesKey (Key: ")?; + for byte in self.key.iter() { + write!(f, "{:0x}", byte)?; + } + write!(f, ", IV: ")?; + for byte in self.iv.iter() { + write!(f, "{:0x}", byte)?; + } + write!(f, ")") + } +} + +/// Encrypt a stream into another using a key +#[cfg(feature="async")] +pub async fn encrypt_stream(key: &AesKey, from: &mut F, to: &mut T) -> Result +where F: AsyncRead + Unpin + ?Sized, + T: AsyncWrite + Unpin + ?Sized +{ + let mut read; + let mut done=0; + + let mut crypter = Crypter::new(Cipher::aes_128_cbc(), Mode::Encrypt, &key.key[..], Some(&key.iv[..]))?; + let mut buffer = [0u8; BUFFER_SIZE]; + let mut crypt_buffer = [0u8; BUFFER_SIZE + BLOCKSIZE]; + while {read = from.read(&mut buffer[..]).await?; read!=0} { + let bytes_encrypted = crypter.update(&buffer[..read], &mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted]).await?; + done += read; + } + + let bytes_encrypted = crypter.finalize(&mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted]).await?; + + Ok(done) +} + +/// Encrypt a stream into another using a key +pub async fn encrypt_stream_sync(key: &AesKey, from: &mut F, to: &mut T) -> Result +where F: io::Read + ?Sized, + T: io::Write + ?Sized +{ + let mut read; + let mut done=0; + + let mut crypter = Crypter::new(Cipher::aes_128_cbc(), Mode::Encrypt, &key.key[..], Some(&key.iv[..]))?; + let mut buffer = [0u8; BUFFER_SIZE]; + let mut crypt_buffer = [0u8; BUFFER_SIZE + BLOCKSIZE]; + while {read = from.read(&mut buffer[..])?; read!=0} { + let bytes_encrypted = crypter.update(&buffer[..read], &mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted])?; + done += read; + } + + let bytes_encrypted = crypter.finalize(&mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted])?; + + Ok(done) +} + +/// Decrypt a stream into another using a key +#[cfg(feature="async")] +pub async fn decrypt_stream(key: &AesKey, from: &mut F, to: &mut T) -> Result +where F: AsyncRead + Unpin + ?Sized, + T: AsyncWrite + Unpin + ?Sized +{ + let mut read; + let mut done=0; + + let mut crypter = Crypter::new(Cipher::aes_128_cbc(), Mode::Decrypt, &key.key[..], Some(&key.iv[..]))?; + let mut buffer = [0u8; BUFFER_SIZE]; + let mut crypt_buffer = [0u8; BUFFER_SIZE + BLOCKSIZE]; + while {read = from.read(&mut buffer[..]).await?; read!=0} { + let bytes_encrypted = crypter.update(&buffer[..read], &mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted]).await?; + done += read; + } + + let bytes_encrypted = crypter.finalize(&mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted]).await?; + + Ok(done) +} + +/// Decrypt a stream into another using a key +pub async fn decrypt_stream_sync(key: &AesKey, from: &mut F, to: &mut T) -> Result +where F: io::Read + ?Sized, + T: io::Write + ?Sized +{ + let mut read; + let mut done=0; + + let mut crypter = Crypter::new(Cipher::aes_128_cbc(), Mode::Decrypt, &key.key[..], Some(&key.iv[..]))?; + let mut buffer = [0u8; BUFFER_SIZE]; + let mut crypt_buffer = [0u8; BUFFER_SIZE + BLOCKSIZE]; + while {read = from.read(&mut buffer[..])?; read!=0} { + let bytes_encrypted = crypter.update(&buffer[..read], &mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted])?; + done += read; + } + + let bytes_encrypted = crypter.finalize(&mut crypt_buffer)?; + to.write(&crypt_buffer[..bytes_encrypted])?; + + Ok(done) +} + +pub use crate::error::aes::Error; diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 0000000..4233c19 --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,48 @@ +//! Bytes related utils +use libc::c_void; +use std::{ + slice, + mem, +}; + +/// Copy slice of bytes only. To copy generic slice, use `util::copy_slice()`. +/// +/// # Notes +/// `dst` and `src` must not overlap. See [move_slice]. +pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize +{ + let sz = std::cmp::min(dst.len(),src.len()); + unsafe { + libc::memcpy(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz); + } + sz +} + +/// Move slice of bytes only +/// +/// # Notes +/// `dst` and `src` can overlap. +pub fn move_slice(dst: &mut [u8], src: &[u8]) -> usize +{ + let sz = std::cmp::min(dst.len(),src.len()); + unsafe { + libc::memmove(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz); + } + sz +} + +/// Get the bytes of a value +pub fn refer(value: &T) -> &[u8] +{ + unsafe { + slice::from_raw_parts(value as *const T as *const u8, mem::size_of_val(value)) + } +} + +/// Get a mutable reference of the bytes of a value +pub fn refer_mut(value: &mut T) -> &mut [u8] +{ + unsafe { + slice::from_raw_parts_mut(value as *mut T as *mut u8, mem::size_of_val(value)) + } +} diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..c6fbfa1 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,22 @@ +//! Constants + +/// Default buffer size for most things +pub const BUFFER_SIZE: usize = 4096; + +/// Size of SHA256 hash checksum in bytes +pub const SHA256_SIZE: usize = 32; + +/// Password saltsize +pub const PASSWORD_SALTSIZE: usize = 32; + +/// Password keysize +pub const PASSWORD_KEYSIZE: usize = 32; + +/// Password rounds +pub const PASSWORD_ROUNDS: u32 = 4096; + +/// Aes key size in bytes +pub const AES_KEYSIZE: usize = 16; + +/// Aes IV size in bytes +pub const AES_IVSIZE: usize = 16; diff --git a/src/crc.rs b/src/crc.rs new file mode 100644 index 0000000..f8d6c7f --- /dev/null +++ b/src/crc.rs @@ -0,0 +1,55 @@ +//! CRC64 algorithm + +use super::consts; +#[allow(unused_imports)] +use std::{ + marker::Unpin, + io, +}; +use crc::{ + crc64, + Hasher64, +}; +#[cfg(feature="async")] +use tokio::{ + prelude::*, + io::AsyncRead, +}; +use consts::BUFFER_SIZE; + +/// Compute a crc64 checksum from a slice. +pub fn compute_slice(data: impl AsRef<[u8]>) -> u64 +{ + let mut digest = crc64::Digest::new(crc64::ECMA); + digest.write(data.as_ref()); + digest.sum64() +} + +/// Read a full stream into a CRC64 checksum +#[cfg(feature="async")] +pub async fn compute_stream(from: &mut T) -> io::Result + where T: AsyncRead + Unpin + ?Sized +{ + let mut buffer = [0u8; BUFFER_SIZE]; + let mut read; + let mut digest = crc64::Digest::new(crc64::ECMA); + while (read = from.read(&mut buffer[..]).await?, read!=0).1 + { + digest.write(&buffer[..read]); + } + Ok(digest.sum64()) +} + +/// Read a full stream into a CRC64 checksum +pub async fn compute_stream_sync(from: &mut T) -> io::Result + where T: io::Read + Unpin + ?Sized +{ + let mut buffer = [0u8; BUFFER_SIZE]; + let mut read; + let mut digest = crc64::Digest::new(crc64::ECMA); + while (read = from.read(&mut buffer[..])?, read!=0).1 + { + digest.write(&buffer[..read]); + } + Ok(digest.sum64()) +} diff --git a/src/error/aes.rs b/src/error/aes.rs new file mode 100644 index 0000000..a97076a --- /dev/null +++ b/src/error/aes.rs @@ -0,0 +1,79 @@ +//! AES errors +use std::{ + io, + fmt, + error, + +}; +use openssl::{ + error::ErrorStack, +}; + +#[derive(Debug)] +pub enum Error +{ + Encrypt, + Decrypt, + Internal(ErrorStack), + IO(io::Error), + Random, + + Length{expected: Option, got: Option}, + + Unknown, +} + +impl error::Error for Error +{ + fn source(&self) -> Option<&(dyn error::Error+'static)> + { + match &self { + Error::Internal(stack) => Some(stack), + Error::IO(io) => Some(io), + _ => None, + } + } +} +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "aes error: ")?; + match self + { + Error::Random => write!(f, "rng failure"), + Error::IO(io) => write!(f, "io: {}", io), + Error::Encrypt => write!(f, "encryption failed"), + Error::Decrypt => write!(f, "decryption failed"), + Error::Internal(ssl) => write!(f, "internal: {}", ssl), + Error::Length{expected: Some(expected), got: Some(got)} => write!(f, "bad length: expected {}, got {}", expected, got), + Error::Length{expected: Some(expected), ..} => write!(f, "bad length: expected {}", expected), + Error::Length{got: Some(got), ..} => write!(f, "bad length: got {}", got), + _ => write!(f, "unknown"), + } + } +} + +impl From for Error +{ + fn from(ssl: ErrorStack) -> Self + { + Self::Internal(ssl) + } +} + +impl From for Error +{ + #[inline] fn from(_: getrandom::Error) -> Self + { + Self::Random + } +} + +impl From for Error +{ + fn from(i: io::Error) -> Self + { + Self::IO(i) + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..173a100 --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1,9 @@ +//! All errors +use std::{ + error, fmt, +}; + +#[cfg(feature="password")] +pub mod password; +#[cfg(feature="aes")] +pub mod aes; diff --git a/src/error/password.rs b/src/error/password.rs new file mode 100644 index 0000000..eb8ccab --- /dev/null +++ b/src/error/password.rs @@ -0,0 +1,27 @@ +//! Password related error +use super::*; + +/// Represents an error regarding password related operations +#[derive(Debug)] +pub enum Error +{ + Random, + Unknown, + Length{expected: Option, got: Option}, +} +impl error::Error for Error{} + +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Error::Random => write!(f, "rng failure"), + Error::Length{expected: Some(expected), got: Some(got)} => write!(f, "bad length: expected {}, got {}", expected, got), + Error::Length{expected: Some(expected), ..} => write!(f, "bad length: expected {}", expected), + Error::Length{got: Some(got), ..} => write!(f, "bad length: got {}", got), + Error::Length{..} => write!(f, "bad length"), + _ => write!(f, "unknown"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..76def3a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +#![allow(dead_code)] + +mod consts; +use consts::*; + +pub mod util; +pub mod bytes; + +mod error; + +// Actual things + +#[cfg(feature="sha256")] +pub mod sha256; +#[cfg(feature="password")] +pub mod password; +#[cfg(feature="aes")] +pub mod aes; +#[cfg(feature="checksum")] +pub mod crc; diff --git a/src/password.rs b/src/password.rs new file mode 100644 index 0000000..e289dd2 --- /dev/null +++ b/src/password.rs @@ -0,0 +1,167 @@ +//! Password related functions +use super::*; +use std::{ + fmt, +}; +use pbkdf2::{ + pbkdf2, +}; +use sha2::{ + Sha256, +}; +use hex_literal::hex; +use hmac::Hmac; +use getrandom::getrandom; + +pub const SALTSIZE: usize = consts::PASSWORD_SALTSIZE; +pub const KEYSIZE: usize = consts::PASSWORD_KEYSIZE; +pub const ROUNDS: u32 = consts::PASSWORD_ROUNDS; + +/// Represents a password hash +#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)] +#[repr(C, packed)] +pub struct Password { + derived: [u8; KEYSIZE], +} + +/// Represents a salt to be used for password operations +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[repr(C, packed)] +pub struct Salt([u8; SALTSIZE]); + +impl Default for Salt +{ + #[inline] + fn default() -> Self + { + Self::embedded() + } +} + +/// The salt value used for `Salt::embedded()`. +pub const STATIC_SALT: [u8; SALTSIZE] = hex!("d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d"); + +impl Salt +{ + /// The default embedded static salt + pub const fn embedded() -> Self + { + Self(STATIC_SALT) + } + + /// Generate a random salt + pub fn random() -> Result + { + let mut output = [0u8; SALTSIZE]; + match getrandom(&mut output[..]) { + Ok(_) => Ok(Self(output)), + Err(_) => Err(Error::Random), + } + } + + /// Create a specific salt + #[inline] pub const fn specific(from: [u8; SALTSIZE]) -> Self + { + Self(from) + } + + /// Create a specific salt from a slice + pub fn slice(from: T) -> Result + where T: AsRef<[u8]> + { + let mut this = Self::none(); + if bytes::copy_slice(&mut this.0[..], from.as_ref()) != this.0.len() { + Err(Error::Length{expected: Some(this.0.len()), got: None}) + } else { + Ok(this) + } + } + + /// An empty salt + #[inline] pub const fn none() -> Self + { + Self([0u8; SALTSIZE]) + } +} + +impl From<[u8; SALTSIZE]> for Salt +{ + #[inline] fn from(from: [u8; SALTSIZE]) -> Self + { + Self::specific(from) + } +} + +impl From for [u8; SALTSIZE] +{ + #[inline] fn from(from: Salt) -> Self + { + from.0 + } +} + +impl AsRef<[u8]> for Salt +{ + fn as_ref(&self) -> &[u8] + { + &self.0[..] + } +} + +impl AsMut<[u8]> for Salt +{ + fn as_mut(&mut self) -> &mut [u8] + { + &mut self.0[..] + } +} + + +impl Password +{ + /// Validate this password. + pub fn validate(&self, string: impl AsRef, salt: &Salt) -> bool + { + &Self::derive(string, salt) == self + } + + /// Derive a password hash from string and salt + pub fn derive(string: impl AsRef, salt: &Salt) -> Password + { + let string = string.as_ref(); + let mut derived = [0u8; KEYSIZE]; + pbkdf2::>(string.as_bytes(), &salt.0[..], ROUNDS, &mut derived[..]); + + Self{derived} + } +} + +impl AsRef<[u8]> for Password +{ + #[inline] fn as_ref(&self) -> &[u8] + { + &self.derived[..] + } +} + +impl AsMut<[u8]> for Password +{ + #[inline] fn as_mut(&mut self) -> &mut [u8] + { + &mut self.derived[..] + } +} + +impl fmt::Display for Password +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + for x in self.derived.iter() + { + write!(f, "{:x}", x)?; + } + Ok(()) + } +} + +pub use crate::error::password::Error; diff --git a/src/sha256.rs b/src/sha256.rs new file mode 100644 index 0000000..ea27ae8 --- /dev/null +++ b/src/sha256.rs @@ -0,0 +1,156 @@ +//! Deals with SHA256 hashing +use super::*; +#[allow(unused_imports)] +use std::{ + fmt, + marker::Unpin, + io, +}; +use sha2::{ + Digest, Sha256, +}; +#[cfg(feature="async")] +use tokio::{ + io::AsyncRead, + prelude::*, +}; + +const SIZE: usize = consts::SHA256_SIZE; + +/// Represents a SHA256 hash +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)] +#[repr(C, packed)] +pub struct Sha256Hash +{ + hash: [u8; SIZE], +} + +impl Sha256Hash +{ + /// Return an empty SHA256 hash container + pub const fn empty() -> Self + { + Self { hash: [0u8; SIZE] } + } + + /// Reads the rest of the stream, and computes SHA256 hash into the current instance. Returning the number of bytes read. + #[cfg(feature="async")] + pub async fn compute_into(&mut self, from: &mut T) -> io::Result + where T: AsyncRead + Unpin + ?Sized + { + let mut buffer = [0u8; super::BUFFER_SIZE]; + let mut hasher = Sha256::new(); + let mut read:usize; + let mut done=0; + while (read = from.read(&mut buffer[..]).await?, read!=0).1 { + hasher.update(&buffer[..read]); + done+=read; + } + + bytes::copy_slice(&mut self.hash[..], &hasher.finalize()); + Ok(done) + } + + /// Reads the rest of the stream, and computes SHA256 hash into the current instance. Returning the number of bytes read. + pub fn compute_into_sync(&mut self, from: &mut T) -> io::Result + where T: io::Read + Unpin + ?Sized + { + let mut buffer = [0u8; super::BUFFER_SIZE]; + let mut hasher = Sha256::new(); + let mut read:usize; + let mut done=0; + while (read = from.read(&mut buffer[..])?, read!=0).1 { + hasher.update(&buffer[..read]); + done+=read; + } + + bytes::copy_slice(&mut self.hash[..], &hasher.finalize()); + Ok(done) + } +} + +impl fmt::Display for Sha256Hash +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "SHA256 (")?; + for x in self.hash.iter() { + write!(f, "{:x}", x)?; + } + write!(f, ")") + } +} + +impl From for Sha256Hash +{ + fn from(from: Sha256) -> Self + { + let mut hash = [0; SIZE]; + bytes::copy_slice(&mut hash, &from.finalize()); + Self{hash} + } +} + +/// Compute the SHA256 hash of the rest of this stream +#[cfg(feature="async")] +pub async fn compute(from: &mut T) -> io::Result +where T: AsyncRead + Unpin + ?Sized +{ + let mut buffer = [0u8; super::BUFFER_SIZE]; + let mut hasher = Sha256::new(); + let mut read:usize; + while (read = from.read(&mut buffer[..]).await?, read!=0).1 { + hasher.update(&buffer[..read]); + } + + let mut hash = [0u8; SIZE]; + bytes::copy_slice(&mut hash[..], &hasher.finalize()); + Ok(Sha256Hash{hash}) +} + + +/// Compute SHA256 hash from a slice. +pub fn compute_slice(from: T) -> Sha256Hash +where T: AsRef<[u8]> +{ + let from = from.as_ref(); + let mut hasher = Sha256::new(); + hasher.update(from); + + let mut hash = [0u8; SIZE]; + bytes::copy_slice(&mut hash, &hasher.finalize()); + Sha256Hash{hash} +} + + +/// Compute the SHA256 hash of the rest of this stream +pub fn compute_sync(from: &mut T) -> io::Result +where T: io::Read + Unpin + ?Sized +{ + let mut buffer = [0u8; super::BUFFER_SIZE]; + let mut hasher = Sha256::new(); + let mut read:usize; + while (read = from.read(&mut buffer[..])?, read!=0).1 { + hasher.update(&buffer[..read]); + } + + let mut hash = [0u8; SIZE]; + bytes::copy_slice(&mut hash[..], &hasher.finalize()); + Ok(Sha256Hash{hash}) +} + +impl AsRef<[u8]> for Sha256Hash +{ + #[inline] fn as_ref(&self) -> &[u8] + { + &self.hash[..] + } +} + +impl AsMut<[u8]> for Sha256Hash +{ + fn as_mut(&mut self) -> &mut [u8] + { + &mut self.hash[..] + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..01a4187 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,26 @@ +//! Utility functions +use std::{ + borrow::{ + Borrow, + ToOwned, + }, +}; + +/// Copy slice `src` into `dst` and return the number of elements copied. +#[inline] pub fn copy_slice(mut dst: V, src: W) -> usize +where V: AsMut<[T]>, + W: AsRef<[U]>, + U: ToOwned, + X: Borrow + Into +{ + + let mut i=0; + for (d, s) in dst.as_mut().iter_mut().zip(src.as_ref().iter()) + { + *d = s.to_owned().into(); + i+=1 + } + i +} + +pub use super::bytes;