amebazii/util.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
//! Utility functions for creating signatures and hashes.
use openssl::md::Md;
use openssl::md_ctx::MdCtx;
use openssl::pkey::PKey;
use std::io;
use crate::error;
/// Skips bytes in the provided reader to ensure that the next read operation aligns
/// with the specified alignment.
///
/// This function calculates the number of bytes to skip in order to make the current
/// position in the stream a multiple of the given `align` value. If the current position
/// is already aligned, no bytes are skipped.
///
/// # Parameters
/// - `reader`: A mutable reference to a reader that implements the `Seek` trait. This
/// is typically a stream or file from which you want to seek.
/// - `align`: The alignment boundary (in bytes) to which the current stream position
/// should be aligned. Must be a power of two.
///
/// # Returns
/// - An `Err(io::Error)` if an I/O error occurs while seeking.
///
/// # Example
/// ```
/// use std::io::{Cursor, Seek, SeekFrom};
/// use amebazii::util::skip_aligned;
///
/// let mut cursor = Cursor::new(vec![0u8; 100]);
/// let align = 16;
/// skip_aligned(&mut cursor, align).unwrap();
/// assert_eq!(cursor.position() % align, 0); // The position should now be aligned to 16.
/// ```
///
/// # Errors
/// This function may return errors related to seeking if the underlying reader does not
/// support seeking or encounters an I/O issue.
pub fn skip_aligned<S>(reader: &mut S, align: u64) -> Result<(), io::Error>
where
S: std::io::Seek,
{
let skip = reader.stream_position()? % align;
if skip > 0 {
reader.seek(io::SeekFrom::Current(align as i64 - skip as i64))?;
}
Ok(())
}
/// Computes an HMAC-MD5 signature for the provided key and data.
///
/// This function generates an HMAC (Hash-based Message Authentication Code) using the MD5
/// hashing algorithm. The key and data are processed, and the resulting 128-bit (16-byte)
/// signature is returned.
///
/// # Parameters
/// - `key`: A byte slice representing the secret key used for the HMAC computation.
/// - `data`: A byte slice containing the data to be authenticated.
///
/// # Returns
/// - `[u8; 16]`: A 16-byte array containing the HMAC-MD5 signature.
/// - `Err(error::Error)`: An error if there is a failure during the HMAC computation (e.g., key or data issues, cryptographic errors).
///
/// # Example
/// ```
/// use amebazii::util::hmac_md5;
///
/// let key = b"secret";
/// let data = b"message";
/// let signature = hmac_md5(key, data).unwrap();
/// assert_eq!(signature.len(), 16); // The HMAC-MD5 signature should be 16 bytes.
/// ```
///
/// # Errors
/// This function may return an error if any step in the HMAC-MD5 computation fails.
pub fn hmac_md5(key: &[u8], data: &[u8]) -> Result<[u8; 16], error::Error> {
let mut signature = [0xFF; 16];
let mut ctx = MdCtx::new()?;
let pkey = PKey::hmac(key)?;
ctx.digest_sign_init(Some(Md::md5()), &pkey)?;
ctx.digest_update(data)?;
ctx.digest_sign_final(Some(&mut signature))?;
return Ok(signature);
}
/// Computes an HMAC-SHA256 signature for the provided key and data.
///
/// This function generates an HMAC (Hash-based Message Authentication Code) using the SHA-256
/// hashing algorithm. The key and data are processed, and the resulting 256-bit (32-byte)
/// signature is returned.
///
/// # Parameters
/// - `key`: A byte slice representing the secret key used for the HMAC computation.
/// - `data`: A byte slice containing the data to be authenticated.
///
/// # Returns
/// - `[u8; 32]`: A 32-byte array containing the HMAC-SHA256 signature.
/// - `Err(error::Error)`: An error if there is a failure during the HMAC computation (e.g., key or data issues, cryptographic errors).
///
/// # Example
/// ```
/// use amebazii::util::hmac_sha256;
///
/// let key = b"secret";
/// let data = b"message";
/// let signature = hmac_sha256(key, data).unwrap();
/// assert_eq!(signature.len(), 32); // The HMAC-SHA256 signature should be 32 bytes.
/// ```
///
/// # Errors
/// This function may return an error if any step in the HMAC-SHA256 computation fails.
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; 32], error::Error> {
let mut signature = [0xFF; 32];
let mut ctx = MdCtx::new()?;
let pkey = PKey::hmac(key)?;
ctx.digest_sign_init(Some(Md::sha256()), &pkey)?;
ctx.digest_update(data)?;
ctx.digest_sign_final(Some(&mut signature))?;
return Ok(signature);
}
/// Computes a SHA-256 hash of the provided data.
///
/// This function computes the SHA-256 hash of the input data. The result is the 256-bit
/// (32-byte) hash.
///
/// # Parameters
/// - `data`: A byte slice containing the data to be hashed.
///
/// # Returns
/// - `[u8; 32]`: A 32-byte array containing the computed SHA-256 hash.
/// - `Err(error::Error)`: An error if there is a failure during the hashing process.
///
/// # Example
/// ```
/// use my_crate::sha256;
/// let data = b"message";
/// let hash = sha256(data).unwrap();
/// assert_eq!(hash.len(), 32); // The SHA-256 hash should be 32 bytes.
/// ```
///
/// # Errors
/// This function may return an error if there is a failure during the SHA-256 hashing process.
pub fn sha256(data: &[u8]) -> Result<[u8; 32], error::Error> {
let mut signature = [0xFF; 32];
let mut ctx = MdCtx::new()?;
ctx.digest_init(Md::sha256())?;
ctx.digest_update(data)?;
ctx.digest_final(&mut signature)?;
return Ok(signature);
}
/// Computes an MD5 hash of the provided data.
///
/// This function computes the MD5 hash of the input data. The result is a 128-bit
/// (16-byte) hash.
///
/// # Parameters
/// - `data`: A byte slice containing the data to be hashed.
///
/// # Returns
/// - `Ok([u8; 16])`: A 16-byte array containing the computed MD5 hash.
/// - `Err(error::Error)`: An error if there is a failure during the hashing process.
///
/// # Example
/// ```
/// use my_crate::md5;
/// let data = b"message";
/// let hash = md5(data).unwrap();
/// assert_eq!(hash.len(), 16); // The MD5 hash should be 16 bytes.
/// ```
///
/// # Errors
/// This function may return an error if there is a failure during the MD5 hashing process.
pub fn md5(data: &[u8]) -> Result<[u8; 16], error::Error> {
let mut signature = [0xFF; 16];
let mut ctx = MdCtx::new()?;
ctx.digest_init(Md::md5())?;
ctx.digest_update(data)?;
ctx.digest_final(&mut signature)?;
return Ok(signature);
}
/// Writes the specified byte `fill` repeatedly to the writer for the given length.
///
/// This function writes the byte `fill` to the writer in chunks, filling the writer
/// with `length` bytes of the specified value. It is more memory-efficient as it avoids
/// allocating a large buffer in memory by writing in smaller chunks.
///
/// # Parameters
/// - `writer`: A mutable reference to a writer that implements the `Write` trait. This can
/// be a file, buffer, or network stream to which the data will be written.
/// - `fill`: The byte value to fill the output with. It will be written repeatedly to the
/// writer.
/// - `length`: The number of times the byte `fill` will be written to the writer.
///
/// # Returns
/// - `Err(io::Error)`: If an I/O error occurs while writing.
///
/// # Example
/// ```
/// use std::io::{Cursor, Write};
/// use my_crate::write_fill;
///
/// let mut cursor = Cursor::new(vec![]);
/// write_fill(&mut cursor, b'A', 10).unwrap();
/// assert_eq!(cursor.into_inner(), vec![b'A'; 10]); // Writes 10 'A' bytes.
/// ```
///
/// # Performance
/// This implementation writes the byte `fill` in smaller chunks, reducing memory usage
/// and making it more efficient for large data sizes.
pub fn write_fill<W>(writer: &mut W, fill: u8, length: u64) -> Result<(), io::Error>
where
W: std::io::Write,
{
const CHUNK_SIZE: usize = 4096;
let mut remaining = length;
let buf = [fill; CHUNK_SIZE];
while remaining > 0 {
let chunk_size = remaining.min(CHUNK_SIZE as u64) as usize;
writer.write_all(&buf[..chunk_size])?;
remaining -= chunk_size as u64;
}
Ok(())
}