initial commit

tokio-1.0
Avril 4 years ago
commit 5a2c84cb68
Signed by: flanchan
GPG Key ID: 284488987C31F630

3
.gitignore vendored

@ -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 @@
RSA

@ -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…
Cancel
Save