mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-31 13:52:29 +00:00
feat(aes): add IV (Initialization Vector) type for CBC mode
Add 128-bit IV type using secret_block! macro with:
- Parsing from hex/binary/ASCII strings
- Conversions to/from Block128 for XOR operations
- Big-endian byte array conversions
This commit is contained in:
parent
220baa09ad
commit
f4480ba218
113
aes/src/iv.rs
Normal file
113
aes/src/iv.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//! Initialization Vector (IV) for AES block cipher modes.
|
||||||
|
//!
|
||||||
|
//! The IV provides randomness to ensure identical plaintexts produce
|
||||||
|
//! different ciphertexts. For CBC mode, the IV must be unpredictable
|
||||||
|
//! and should never be reused with the same key.
|
||||||
|
|
||||||
|
use crate::Block128;
|
||||||
|
use cipher_core::{BlockError, parse_block_int, secret_block};
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
secret_block! {
|
||||||
|
/// 128-bit Initialization Vector for AES cipher modes.
|
||||||
|
///
|
||||||
|
/// Used in CBC mode to XOR with the first plaintext block before encryption.
|
||||||
|
/// Each subsequent block is XORed with the previous ciphertext block.
|
||||||
|
///
|
||||||
|
/// # Security
|
||||||
|
///
|
||||||
|
/// - IVs must be unpredictable (use a cryptographically secure RNG)
|
||||||
|
/// - Never reuse an IV with the same key
|
||||||
|
/// - The IV does not need to be secret, but must be unique
|
||||||
|
pub struct Iv(u128, 128, 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iv {
|
||||||
|
/// Creates an IV from big-endian bytes.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn from_be_bytes(bytes: [u8; 16]) -> Self {
|
||||||
|
Self(u128::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the IV as big-endian bytes.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn to_be_bytes(self) -> [u8; 16] {
|
||||||
|
self.0.to_be_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this IV to a Block128 for XOR operations.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn to_block(self) -> Block128 {
|
||||||
|
Block128::new(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Iv {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:032X}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Iv {
|
||||||
|
type Err = BlockError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(parse_block_int::<u128>(s)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 16]> for Iv {
|
||||||
|
fn from(bytes: [u8; 16]) -> Self {
|
||||||
|
Self::from_be_bytes(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Block128> for Iv {
|
||||||
|
fn from(block: Block128) -> Self {
|
||||||
|
Self(block.as_u128())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Iv> for Block128 {
|
||||||
|
fn from(iv: Iv) -> Self {
|
||||||
|
Self::new(iv.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use claims::assert_ok;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iv_from_hex_string() {
|
||||||
|
let iv = assert_ok!("0x000102030405060708090A0B0C0D0E0F".parse::<Iv>());
|
||||||
|
assert_eq!(iv.as_u128(), 0x0001_0203_0405_0607_0809_0A0B_0C0D_0E0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iv_to_block_conversion() {
|
||||||
|
let iv = Iv::new(0x0011_2233_4455_6677_8899_AABB_CCDD_EEFF);
|
||||||
|
let block = Block128::from(iv);
|
||||||
|
assert_eq!(block.as_u128(), 0x0011_2233_4455_6677_8899_AABB_CCDD_EEFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iv_from_bytes() {
|
||||||
|
let bytes = [
|
||||||
|
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
|
||||||
|
0xEE, 0xFF,
|
||||||
|
];
|
||||||
|
let iv = Iv::from_be_bytes(bytes);
|
||||||
|
assert_eq!(iv.as_u128(), 0x0011_2233_4455_6677_8899_AABB_CCDD_EEFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iv_display_format() {
|
||||||
|
let iv = Iv::new(0x0011_2233_4455_6677_8899_AABB_CCDD_EEFF);
|
||||||
|
assert_eq!(format!("{iv}"), "00112233445566778899AABBCCDDEEFF");
|
||||||
|
assert_eq!(format!("{iv:x}"), "00112233445566778899aabbccddeeff");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,8 +14,9 @@
|
|||||||
mod aes;
|
mod aes;
|
||||||
mod block;
|
mod block;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
mod iv;
|
||||||
mod key;
|
mod key;
|
||||||
mod operations;
|
mod operations;
|
||||||
mod sbox;
|
mod sbox;
|
||||||
|
|
||||||
pub use {aes::Aes, block::Block128, block::Block32};
|
pub use {aes::Aes, block::Block128, block::Block32, iv::Iv};
|
||||||
|
|||||||
@ -8,5 +8,8 @@ edition.workspace = true
|
|||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
zeroize.workspace = true
|
zeroize.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
claims.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user