commit
5a2c84cb68
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
*~
|
@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "cryptohelpers"
|
||||
version = "0.1.0"
|
||||
authors = ["Avril <flanchan@cumallover.me>"]
|
||||
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"
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Self, Error>
|
||||
{
|
||||
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<Self,Error>
|
||||
{
|
||||
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<F,T>(key: &AesKey, from: &mut F, to: &mut T) -> Result<usize, Error>
|
||||
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<F,T>(key: &AesKey, from: &mut F, to: &mut T) -> Result<usize, Error>
|
||||
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<F,T>(key: &AesKey, from: &mut F, to: &mut T) -> Result<usize, Error>
|
||||
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<F,T>(key: &AesKey, from: &mut F, to: &mut T) -> Result<usize, Error>
|
||||
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;
|
@ -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<T: ?Sized>(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<T: ?Sized>(value: &mut T) -> &mut [u8]
|
||||
{
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(value as *mut T as *mut u8, mem::size_of_val(value))
|
||||
}
|
||||
}
|
@ -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;
|
@ -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<T>(from: &mut T) -> io::Result<u64>
|
||||
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<T>(from: &mut T) -> io::Result<u64>
|
||||
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())
|
||||
}
|
@ -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<usize>, got: Option<usize>},
|
||||
|
||||
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<ErrorStack> for Error
|
||||
{
|
||||
fn from(ssl: ErrorStack) -> Self
|
||||
{
|
||||
Self::Internal(ssl)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<getrandom::Error> for Error
|
||||
{
|
||||
#[inline] fn from(_: getrandom::Error) -> Self
|
||||
{
|
||||
Self::Random
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error
|
||||
{
|
||||
fn from(i: io::Error) -> Self
|
||||
{
|
||||
Self::IO(i)
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
//! All errors
|
||||
use std::{
|
||||
error, fmt,
|
||||
};
|
||||
|
||||
#[cfg(feature="password")]
|
||||
pub mod password;
|
||||
#[cfg(feature="aes")]
|
||||
pub mod aes;
|
@ -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<usize>, got: Option<usize>},
|
||||
}
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
@ -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<Self, Error>
|
||||
{
|
||||
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<T>(from: T) -> Result<Self, Error>
|
||||
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<Salt> 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<str>, salt: &Salt) -> bool
|
||||
{
|
||||
&Self::derive(string, salt) == self
|
||||
}
|
||||
|
||||
/// Derive a password hash from string and salt
|
||||
pub fn derive(string: impl AsRef<str>, salt: &Salt) -> Password
|
||||
{
|
||||
let string = string.as_ref();
|
||||
let mut derived = [0u8; KEYSIZE];
|
||||
pbkdf2::<Hmac<Sha256>>(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;
|
@ -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<T>(&mut self, from: &mut T) -> io::Result<usize>
|
||||
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<T>(&mut self, from: &mut T) -> io::Result<usize>
|
||||
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<Sha256> 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<T>(from: &mut T) -> io::Result<Sha256Hash>
|
||||
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<T>(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<T>(from: &mut T) -> io::Result<Sha256Hash>
|
||||
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[..]
|
||||
}
|
||||
}
|
@ -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<T,U,V,W,X>(mut dst: V, src: W) -> usize
|
||||
where V: AsMut<[T]>,
|
||||
W: AsRef<[U]>,
|
||||
U: ToOwned<Owned=X>,
|
||||
X: Borrow<U> + Into<T>
|
||||
{
|
||||
|
||||
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;
|
Loading…
Reference in new issue