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