OneLauncher-Cracked/packages/core/src/utils/crypto.rs
2025-06-09 18:41:39 +02:00

122 lines
No EOL
2.9 KiB
Rust

use std::fmt::{Display, Write};
use std::io::Read;
use std::str::FromStr;
use digest::DynDigest;
use serde::{Deserialize, Serialize};
use tokio::io::AsyncReadExt;
use super::io::IOError;
#[onelauncher_macro::error]
#[derive(Debug, thiserror::Error)]
pub enum CryptoError {
#[error("expected {algorithm} hash {expected}, but got {actual}")]
InvalidHash {
algorithm: HashAlgorithm,
expected: String,
actual: String,
},
#[error("invalid hash algorithm")]
InvalidAlgorithm,
}
#[onelauncher_macro::specta]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum HashAlgorithm {
Sha1,
Sha256,
Md5,
}
const BUFFER_SIZE: usize = 1024 * 256; // 16 KiB
impl HashAlgorithm {
pub async fn hash(&self, data: &[u8]) -> Result<String, IOError> {
let mut hasher = get_inner(self);
hasher.update(data);
Ok(to_hex(&hasher.finalize()))
}
pub fn hash_sync(&self, data: &[u8]) -> Result<String, IOError> {
let mut hasher = get_inner(self);
hasher.update(data);
Ok(to_hex(&hasher.finalize()))
}
pub async fn hash_file(&self, path: impl AsRef<std::path::Path>) -> Result<String, IOError> {
let path = path.as_ref();
let mut hasher = get_inner(self);
let file = tokio::fs::File::open(path).await?;
let mut reader = tokio::io::BufReader::new(file);
let mut buffer = vec![0; BUFFER_SIZE].into_boxed_slice();
loop {
let bytes_read = reader.read(&mut buffer).await?;
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
Ok(to_hex(&hasher.finalize()))
}
pub fn hash_file_sync(&self, path: impl AsRef<std::path::Path>) -> Result<String, IOError> {
let path = path.as_ref();
let mut hasher = get_inner(self);
let mut file = std::fs::File::open(path)?;
let mut buffer = vec![0; BUFFER_SIZE].into_boxed_slice();
loop {
let bytes_read = file.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
Ok(to_hex(&hasher.finalize()))
}
}
impl Display for HashAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Sha1 => write!(f, "sha1"),
Self::Sha256 => write!(f, "sha256"),
Self::Md5 => write!(f, "md5"),
}
}
}
impl FromStr for HashAlgorithm {
type Err = CryptoError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"sha1" | "sha-1" => Ok(Self::Sha1),
"sha256" | "sha-256" => Ok(Self::Sha256),
"md5" | "md-5" => Ok(Self::Md5),
_ => Err(CryptoError::InvalidAlgorithm),
}
}
}
fn get_inner(algorithm: &HashAlgorithm) -> Box<dyn DynDigest + Send + Sync> {
match algorithm {
HashAlgorithm::Sha1 => Box::new(sha1::Sha1::default()),
HashAlgorithm::Sha256 => Box::new(sha2::Sha256::default()),
HashAlgorithm::Md5 => Box::new(md5::Md5::default())
}
}
fn to_hex(data: &[u8]) -> String {
data.iter().fold(String::new(), |mut output, b| {
let _ = write!(output, "{b:02x}");
output
})
}