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