diff --git a/keetanetwork-account/src/account.rs b/keetanetwork-account/src/account.rs index 791ded3..042d4e2 100644 --- a/keetanetwork-account/src/account.rs +++ b/keetanetwork-account/src/account.rs @@ -887,52 +887,68 @@ pub struct KeyECDSASECP256K1 { pub public_key: Secp256k1PublicKey, } -impl KeyPair for KeyECDSASECP256K1 { - type PublicKey = Secp256k1PublicKey; - const KEY_PAIR_TYPE: KeyPairType = KeyPairType::ECDSASECP256K1; +/// Macro to implement [`KeyPair`] for cryptographic key types whose private +/// keys derive via the crypto crate's seed derivation and support ECIES +/// decryption. +macro_rules! impl_crypto_keypair { + ($key_type:ty, $public_key:ty, $keypair_type:expr, $derivation:ty, $any_variant:ident, $algorithm:expr) => { + impl KeyPair for $key_type { + type PublicKey = $public_key; + const KEY_PAIR_TYPE: KeyPairType = $keypair_type; - fn seed_to_private_key(seed: &Seed, index: Index) -> Result { - // Convert seed and index to bytes for HKDF - let seed_buffer = combine_seed_and_index(seed, index).into_secret(); - // Use the crypto crate's secp256k1 derivation - let private_key = Secp256k1Derivation::derive_from_seed(seed_buffer)?; + fn seed_to_private_key(seed: &Seed, index: Index) -> Result { + // Convert seed and index to bytes for HKDF + let seed_buffer = combine_seed_and_index(seed, index).into_secret(); + // Use the crypto crate's derivation for this algorithm + let private_key = <$derivation>::derive_from_seed(seed_buffer)?; - Ok(AnyPrivateKey::Secp256k1(private_key)) - } + Ok(AnyPrivateKey::$any_variant(private_key)) + } - fn derive_public_key_string(key: &AnyPrivateKey) -> Result { - if let AnyPrivateKey::Secp256k1(secp_key) = key { - let public_key = secp_key.as_public_key(); - let public_key_bytes = Vec::::from(&public_key); - format_public_key_string(&public_key_bytes, Algorithm::Secp256k1 as u8) - } else { - Err(AccountError::InvalidConstruction) - } - } + fn derive_public_key_string(key: &AnyPrivateKey) -> Result { + if let AnyPrivateKey::$any_variant(private_key) = key { + let public_key = private_key.as_public_key(); + let public_key_bytes = Vec::::from(&public_key); - fn decrypt>(&self, ciphertext: T) -> Result, AccountError> { - let private_key = self - .private_key - .as_ref() - .ok_or(AccountError::InvalidConstruction)?; + format_public_key_string(&public_key_bytes, $algorithm as u8) + } else { + Err(AccountError::InvalidConstruction) + } + } - let plaintext = private_key.decrypt(ciphertext.as_ref())?; - Ok(plaintext) - } + fn decrypt>(&self, ciphertext: T) -> Result, AccountError> { + let private_key = self + .private_key + .as_ref() + .ok_or(AccountError::InvalidConstruction)?; - fn supports_encryption(&self) -> bool { - true // ECDSA secp256k1 supports ECIES encryption - } + Ok(private_key.decrypt(ciphertext.as_ref())?) + } - fn to_public_key(&self) -> Self::PublicKey { - self.public_key.clone() - } + fn supports_encryption(&self) -> bool { + true // ECIES encryption is implemented for this algorithm + } - fn take_private_key(self) -> Option { - self.private_key.map(AnyPrivateKey::Secp256k1) - } + fn to_public_key(&self) -> Self::PublicKey { + self.public_key.clone() + } + + fn take_private_key(self) -> Option { + self.private_key.map(AnyPrivateKey::$any_variant) + } + } + }; } +impl_crypto_keypair!( + KeyECDSASECP256K1, + Secp256k1PublicKey, + KeyPairType::ECDSASECP256K1, + Secp256k1Derivation, + Secp256k1, + Algorithm::Secp256k1 +); + /// ECDSA key pair using the secp256r1 elliptic curve (NIST P-256). /// /// This key type implements ECDSA (Elliptic Curve Digital Signature Algorithm) @@ -965,49 +981,14 @@ pub struct KeyECDSASECP256R1 { pub public_key: Secp256r1PublicKey, } -impl KeyPair for KeyECDSASECP256R1 { - type PublicKey = Secp256r1PublicKey; - const KEY_PAIR_TYPE: KeyPairType = KeyPairType::ECDSASECP256R1; - - fn seed_to_private_key(seed: &Seed, index: Index) -> Result { - // Convert seed and index to bytes for HKDF - let seed_buffer = combine_seed_and_index(seed, index).into_secret(); - // Use the crypto crate's secp256r1 derivation - let private_key = Secp256r1Derivation::derive_from_seed(seed_buffer)?; - Ok(AnyPrivateKey::Secp256r1(private_key)) - } - - fn derive_public_key_string(key: &AnyPrivateKey) -> Result { - if let AnyPrivateKey::Secp256r1(secp_key) = key { - let public_key = secp_key.as_public_key(); - let public_key_bytes = Vec::::from(&public_key); - format_public_key_string(&public_key_bytes, Algorithm::Secp256r1 as u8) - } else { - Err(AccountError::InvalidConstruction) - } - } - - fn decrypt>(&self, ciphertext: T) -> Result, AccountError> { - let private_key = self - .private_key - .as_ref() - .ok_or(AccountError::InvalidConstruction)?; - - Ok(private_key.decrypt(ciphertext.as_ref())?) - } - - fn supports_encryption(&self) -> bool { - true // ECIES-secp256r1-AES256CBC is now implemented - } - - fn to_public_key(&self) -> Self::PublicKey { - self.public_key.clone() - } - - fn take_private_key(self) -> Option { - self.private_key.map(AnyPrivateKey::Secp256r1) - } -} +impl_crypto_keypair!( + KeyECDSASECP256R1, + Secp256r1PublicKey, + KeyPairType::ECDSASECP256R1, + Secp256r1Derivation, + Secp256r1, + Algorithm::Secp256r1 +); /// Ed25519 digital signature key pair implementation. /// @@ -1044,50 +1025,14 @@ pub struct KeyED25519 { pub public_key: Ed25519PublicKey, } -impl KeyPair for KeyED25519 { - type PublicKey = Ed25519PublicKey; - const KEY_PAIR_TYPE: KeyPairType = KeyPairType::ED25519; - - fn seed_to_private_key(seed: &Seed, index: Index) -> Result { - // Convert seed and index to bytes for HKDF - let seed_buffer = combine_seed_and_index(seed, index).into_secret(); - // Use the crypto crate's Ed25519 derivation - let private_key = Ed25519Derivation::derive_from_seed(seed_buffer)?; - Ok(AnyPrivateKey::Ed25519(private_key)) - } - - fn derive_public_key_string(key: &AnyPrivateKey) -> Result { - if let AnyPrivateKey::Ed25519(ed_key) = key { - let public_key = ed_key.verifying_key(); - let public_key_bytes = Vec::::from(&public_key); - let formatted_key = format_public_key_string(&public_key_bytes, Algorithm::Ed25519 as u8)?; - Ok(formatted_key) - } else { - Err(AccountError::InvalidConstruction) - } - } - - fn decrypt>(&self, ciphertext: T) -> Result, AccountError> { - let private_key = self - .private_key - .as_ref() - .ok_or(AccountError::InvalidConstruction)?; - - Ok(private_key.decrypt(ciphertext.as_ref())?) - } - - fn supports_encryption(&self) -> bool { - true // ECIES-25519 via X25519 is now implemented - } - - fn to_public_key(&self) -> Self::PublicKey { - self.public_key.clone() - } - - fn take_private_key(self) -> Option { - self.private_key.map(AnyPrivateKey::Ed25519) - } -} +impl_crypto_keypair!( + KeyED25519, + Ed25519PublicKey, + KeyPairType::ED25519, + Ed25519Derivation, + Ed25519, + Algorithm::Ed25519 +); /// Network identifier key for node addressing and discovery. /// diff --git a/keetanetwork-asn1/src/rasn.rs b/keetanetwork-asn1/src/rasn.rs index 12f03dc..10ac4d2 100644 --- a/keetanetwork-asn1/src/rasn.rs +++ b/keetanetwork-asn1/src/rasn.rs @@ -167,109 +167,67 @@ impl_try_from_rasn_encode!(FeesSingle); impl_try_from_rasn_encode!(FeesMultiple); impl_try_from_rasn_encode!(Extension); -// Implement der traits for AlgorithmIdentifier to make it compatible with x509 certificate structures -impl<'a> DecodeValue<'a> for AlgorithmIdentifier { - fn decode_value>(reader: &mut R, header: Header) -> der::Result { - // Read the content bytes - let content_bytes = reader.read_slice(header.length)?; - // Reconstruct the complete DER structure with the SEQUENCE tag and length - let mut der_bytes = Vec::new(); - // SEQUENCE tag (0x30) - der_bytes.push(0x30); - - // Length encoding - let length_value = usize::try_from(header.length).map_err(|_| der::ErrorKind::Failed)?; - if length_value < 0x80 { - der_bytes.push(length_value as u8); - } else { - // Long form length encoding - let length_bytes = length_value.to_be_bytes(); - let significant_bytes = length_bytes.iter().skip_while(|&&b| b == 0).count(); - der_bytes.push(0x80 | significant_bytes as u8); - der_bytes.extend_from_slice(&length_bytes[8 - significant_bytes..]); +/// Macro to bridge rasn-backed SEQUENCE types into the der crate's trait system +/// (DecodeValue/EncodeValue/Sequence/ValueOrd), making them usable inside x509 +/// certificate structures. +macro_rules! impl_der_bridge { + ($type:ty) => { + impl<'a> DecodeValue<'a> for $type { + fn decode_value>(reader: &mut R, header: Header) -> der::Result { + // Read the content bytes + let content_bytes = reader.read_slice(header.length)?; + // Reconstruct the complete DER structure with the SEQUENCE tag and length + let mut der_bytes = Vec::new(); + + // SEQUENCE tag (0x30) + der_bytes.push(0x30); + + // Length encoding + let length_value = usize::try_from(header.length).map_err(|_| der::ErrorKind::Failed)?; + if length_value < 0x80 { + der_bytes.push(length_value as u8); + } else { + // Long form length encoding + let length_bytes = length_value.to_be_bytes(); + let significant_bytes = length_bytes.iter().skip_while(|&&b| b == 0).count(); + der_bytes.push(0x80 | significant_bytes as u8); + der_bytes.extend_from_slice(&length_bytes[8 - significant_bytes..]); + } + + // Content bytes + der_bytes.extend_from_slice(content_bytes); + + rasn::der::decode::(&der_bytes).map_err(|_| der::ErrorKind::Failed.into()) + } } - // Content bytes - der_bytes.extend_from_slice(content_bytes); - - rasn::der::decode::(&der_bytes).map_err(|_| der::ErrorKind::Failed.into()) - } -} - -impl EncodeValue for AlgorithmIdentifier { - fn value_len(&self) -> der::Result { - let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - Length::try_from(der_bytes.len()).map_err(|_| der::ErrorKind::Failed.into()) - } - - fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { - let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - writer.write(&der_bytes) - } -} - -impl<'a> Sequence<'a> for AlgorithmIdentifier {} - -impl ValueOrd for AlgorithmIdentifier { - fn value_cmp(&self, other: &Self) -> der::Result { - // Compare based on DER encoding - let self_der = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - let other_der = rasn::der::encode(other).map_err(|_| der::ErrorKind::Failed)?; - Ok(self_der.cmp(&other_der)) - } -} + impl EncodeValue for $type { + fn value_len(&self) -> der::Result { + let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; + Length::try_from(der_bytes.len()).map_err(|_| der::ErrorKind::Failed.into()) + } -// Implement der traits for SubjectPublicKeyInfo -impl<'a> DecodeValue<'a> for SubjectPublicKeyInfo { - fn decode_value>(reader: &mut R, header: Header) -> der::Result { - // Read the content bytes - let content_bytes = reader.read_slice(header.length)?; - // Reconstruct the complete DER structure with the SEQUENCE tag and length - let mut der_bytes = Vec::new(); - // SEQUENCE tag (0x30) - der_bytes.push(0x30); - - // Length encoding - let length_value = usize::try_from(header.length).map_err(|_| der::ErrorKind::Failed)?; - if length_value < 0x80 { - der_bytes.push(length_value as u8); - } else { - // Long form length encoding - let length_bytes = length_value.to_be_bytes(); - let significant_bytes = length_bytes.iter().skip_while(|&&b| b == 0).count(); - der_bytes.push(0x80 | significant_bytes as u8); - der_bytes.extend_from_slice(&length_bytes[8 - significant_bytes..]); + fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { + let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; + writer.write(&der_bytes) + } } - // Content bytes - der_bytes.extend_from_slice(content_bytes); - - rasn::der::decode::(&der_bytes).map_err(|_| der::ErrorKind::Failed.into()) - } -} - -impl EncodeValue for SubjectPublicKeyInfo { - fn value_len(&self) -> der::Result { - let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - Length::try_from(der_bytes.len()).map_err(|_| der::ErrorKind::Failed.into()) - } + impl<'a> Sequence<'a> for $type {} - fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { - let der_bytes = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - writer.write(&der_bytes) - } + impl ValueOrd for $type { + fn value_cmp(&self, other: &Self) -> der::Result { + // Compare based on DER encoding + let self_der = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; + let other_der = rasn::der::encode(other).map_err(|_| der::ErrorKind::Failed)?; + Ok(self_der.cmp(&other_der)) + } + } + }; } -impl<'a> Sequence<'a> for SubjectPublicKeyInfo {} - -impl ValueOrd for SubjectPublicKeyInfo { - fn value_cmp(&self, other: &Self) -> der::Result { - // Compare based on DER encoding - let self_der = rasn::der::encode(self).map_err(|_| der::ErrorKind::Failed)?; - let other_der = rasn::der::encode(other).map_err(|_| der::ErrorKind::Failed)?; - Ok(self_der.cmp(&other_der)) - } -} +impl_der_bridge!(AlgorithmIdentifier); +impl_der_bridge!(SubjectPublicKeyInfo); /// Extension trait for ObjectIdentifier to provide compatibility between rasn and der backends pub trait ObjectIdentifierExt { diff --git a/keetanetwork-crypto/src/algorithms/aes_cbc.rs b/keetanetwork-crypto/src/algorithms/aes_cbc.rs index de97f2a..13d9ecf 100644 --- a/keetanetwork-crypto/src/algorithms/aes_cbc.rs +++ b/keetanetwork-crypto/src/algorithms/aes_cbc.rs @@ -7,9 +7,9 @@ use alloc::vec::Vec; use aes::Aes256; use cbc::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; use cbc::{Decryptor, Encryptor}; -use rand_core::{OsRng, TryRngCore}; -use crate::error::{CryptoError, OrCryptoError}; +use crate::algorithms::aes_common::{ensure_key_size, prepend_iv, resolve_iv, split_iv}; +use crate::error::CryptoError; use crate::operations::encryption::SymmetricEncryption; /// AES-256-CBC symmetric encryption implementation. @@ -42,41 +42,17 @@ impl SymmetricEncryption for Aes256Cbc { plaintext: P, ) -> Result, CryptoError> { let key = key.as_ref(); - let plaintext = plaintext.as_ref(); - - if key.len() != 32 { - return Err(CryptoError::InvalidKeySize); - } + ensure_key_size(key, 32)?; // Use provided IV or generate random one - let iv_bytes = match iv { - Some(iv_slice) => { - if iv_slice.len() != 16 { - return Err(CryptoError::InvalidIvSize); - } - - let mut iv_array = [0u8; 16]; - iv_array.copy_from_slice(iv_slice); - iv_array - } - None => { - let mut iv_array = [0u8; 16]; - OsRng.try_fill_bytes(&mut iv_array).or_encryption_failed()?; - iv_array - } - }; - + let iv_bytes = resolve_iv(iv)?; // Create cipher let cipher = Aes256CbcEnc::new_from_slices(key, &iv_bytes)?; // Encrypt with PKCS#7 padding - let ciphertext = cipher.encrypt_padded_vec_mut::(plaintext); + let ciphertext = cipher.encrypt_padded_vec_mut::(plaintext.as_ref()); // Return IV + ciphertext - let mut result = Vec::with_capacity(16 + ciphertext.len()); - result.extend_from_slice(&iv_bytes); - result.extend_from_slice(&ciphertext); - - Ok(result) + Ok(prepend_iv(&iv_bytes, &ciphertext)) } /// Decrypt data using AES-256-CBC. @@ -85,19 +61,10 @@ impl SymmetricEncryption for Aes256Cbc { /// Decrypted data with PKCS#7 padding removed. fn decrypt, C: AsRef<[u8]>>(&self, key: K, ciphertext: C) -> Result, CryptoError> { let key = key.as_ref(); - if key.len() != 32 { - return Err(CryptoError::InvalidKeySize); - } - - let ciphertext = ciphertext.as_ref(); - if ciphertext.len() < 16 { - return Err(CryptoError::DecryptionFailed); - } - - // Extract IV and ciphertext - let iv = &ciphertext[0..16]; - // Ensure ciphertext is long enough - let encrypted_data = &ciphertext[16..]; + ensure_key_size(key, 32)?; + + // Extract IV and encrypted payload + let (iv, encrypted_data) = split_iv(ciphertext.as_ref())?; // Create cipher let cipher = Aes256CbcDec::new_from_slices(key, iv)?; diff --git a/keetanetwork-crypto/src/algorithms/aes_common.rs b/keetanetwork-crypto/src/algorithms/aes_common.rs new file mode 100644 index 0000000..ce05315 --- /dev/null +++ b/keetanetwork-crypto/src/algorithms/aes_common.rs @@ -0,0 +1,56 @@ +//! Shared helpers for AES cipher modes that transport the IV alongside the +//! ciphertext (IV prepended to the encrypted payload). + +use alloc::vec::Vec; + +use rand_core::{OsRng, TryRngCore}; + +use crate::error::CryptoError; + +/// AES block/IV size in bytes. +pub(crate) const IV_SIZE: usize = 16; + +/// Validate that a key has the expected length. +pub(crate) fn ensure_key_size(key: &[u8], expected: usize) -> Result<(), CryptoError> { + if key.len() != expected { + return Err(CryptoError::InvalidKeySize); + } + + Ok(()) +} + +/// Resolve the IV for encryption: validate a caller-provided IV or generate a +/// cryptographically random one. +pub(crate) fn resolve_iv(iv: Option<&[u8]>) -> Result<[u8; IV_SIZE], CryptoError> { + let mut iv_array = [0u8; IV_SIZE]; + match iv { + Some(iv_slice) => { + if iv_slice.len() != IV_SIZE { + return Err(CryptoError::InvalidIvSize); + } + + iv_array.copy_from_slice(iv_slice); + } + None => OsRng.try_fill_bytes(&mut iv_array)?, + } + + Ok(iv_array) +} + +/// Assemble the transport format: IV followed by ciphertext. +pub(crate) fn prepend_iv(iv: &[u8; IV_SIZE], ciphertext: &[u8]) -> Vec { + let mut result = Vec::with_capacity(IV_SIZE + ciphertext.len()); + result.extend_from_slice(iv); + result.extend_from_slice(ciphertext); + + result +} + +/// Split the transport format back into (IV, encrypted payload). +pub(crate) fn split_iv(ciphertext: &[u8]) -> Result<(&[u8], &[u8]), CryptoError> { + if ciphertext.len() < IV_SIZE { + return Err(CryptoError::DecryptionFailed); + } + + Ok(ciphertext.split_at(IV_SIZE)) +} diff --git a/keetanetwork-crypto/src/algorithms/aes_ctr.rs b/keetanetwork-crypto/src/algorithms/aes_ctr.rs index 6c3be34..f364f08 100644 --- a/keetanetwork-crypto/src/algorithms/aes_ctr.rs +++ b/keetanetwork-crypto/src/algorithms/aes_ctr.rs @@ -7,8 +7,8 @@ use alloc::vec::Vec; use aes::Aes128; use ctr::cipher::{KeyIvInit, StreamCipher}; use ctr::Ctr128BE; -use rand_core::{OsRng, TryRngCore}; +use crate::algorithms::aes_common::{ensure_key_size, prepend_iv, resolve_iv, split_iv}; use crate::error::CryptoError; use crate::operations::encryption::SymmetricEncryption; @@ -43,31 +43,20 @@ impl Aes128CtrCipher { /// Generate a random IV for CTR mode pub fn generate_iv() -> Result<[u8; 16], CryptoError> { - let mut iv = [0u8; 16]; - OsRng.try_fill_bytes(&mut iv)?; - - Ok(iv) + resolve_iv(None) } - /// Encrypt data with a provided IV. - /// - /// # Arguments - /// * `key` - 16-byte encryption key - /// * `iv` - 16-byte initialization vector - /// * `plaintext` - Data to encrypt + /// Apply the CTR keystream to data. /// - /// # Returns - /// Encrypted data (same length as plaintext) - pub fn encrypt_with_iv, I: AsRef<[u8]>, P: AsRef<[u8]>>( - &self, + /// CTR mode is symmetric: the same operation performs both encryption + /// and decryption. + fn apply_keystream, I: AsRef<[u8]>, D: AsRef<[u8]>>( key: K, iv: I, - plaintext: P, + data: D, ) -> Result, CryptoError> { let key = key.as_ref(); - if key.len() != 16 { - return Err(CryptoError::InvalidKeySize); - } + ensure_key_size(key, 16)?; let iv = iv.as_ref(); if iv.len() != 16 { @@ -77,11 +66,29 @@ impl Aes128CtrCipher { // Create CTR cipher instance let mut cipher = Aes128Ctr::new_from_slices(key, iv)?; // CTR mode works in-place, so we need a mutable copy - let mut ciphertext = plaintext.as_ref().to_vec(); + let mut output = data.as_ref().to_vec(); - cipher.apply_keystream(&mut ciphertext); + cipher.apply_keystream(&mut output); - Ok(ciphertext) + Ok(output) + } + + /// Encrypt data with a provided IV. + /// + /// # Arguments + /// * `key` - 16-byte encryption key + /// * `iv` - 16-byte initialization vector + /// * `plaintext` - Data to encrypt + /// + /// # Returns + /// Encrypted data (same length as plaintext) + pub fn encrypt_with_iv, I: AsRef<[u8]>, P: AsRef<[u8]>>( + &self, + key: K, + iv: I, + plaintext: P, + ) -> Result, CryptoError> { + Self::apply_keystream(key, iv, plaintext) } /// Decrypt data with a provided IV. @@ -99,24 +106,7 @@ impl Aes128CtrCipher { iv: I, ciphertext: C, ) -> Result, CryptoError> { - let key = key.as_ref(); - if key.len() != 16 { - return Err(CryptoError::InvalidKeySize); - } - - let iv = iv.as_ref(); - if iv.len() != 16 { - return Err(CryptoError::InvalidOperation); - } - - // Create CTR cipher instance - let mut cipher = Aes128Ctr::new_from_slices(key, iv)?; - // CTR mode is symmetric - decryption is the same as encryption - let mut plaintext = ciphertext.as_ref().to_vec(); - - cipher.apply_keystream(&mut plaintext); - - Ok(plaintext) + Self::apply_keystream(key, iv, ciphertext) } } @@ -131,33 +121,16 @@ impl SymmetricEncryption for Aes128CtrCipher { plaintext: P, ) -> Result, CryptoError> { let key = key.as_ref(); - if key.len() != 16 { - return Err(CryptoError::InvalidKeySize); - } + ensure_key_size(key, 16)?; // Use provided IV or generate random one - let iv_bytes = match iv { - Some(iv_slice) => { - if iv_slice.len() != 16 { - return Err(CryptoError::InvalidIvSize); - } - - let mut iv_array = [0u8; 16]; - iv_array.copy_from_slice(iv_slice); - iv_array - } - None => Self::generate_iv()?, - }; + let iv_bytes = resolve_iv(iv)?; // Encrypt with the IV let ciphertext = self.encrypt_with_iv(key, iv_bytes, plaintext)?; // Prepend IV to ciphertext - let mut result = Vec::with_capacity(16 + ciphertext.len()); - result.extend_from_slice(&iv_bytes); - result.extend_from_slice(&ciphertext); - - Ok(result) + Ok(prepend_iv(&iv_bytes, &ciphertext)) } /// Decrypt data with IV extracted from the beginning @@ -165,18 +138,10 @@ impl SymmetricEncryption for Aes128CtrCipher { /// Expected format: iv (16 bytes) + ciphertext fn decrypt, C: AsRef<[u8]>>(&self, key: K, ciphertext: C) -> Result, CryptoError> { let key = key.as_ref(); - if key.len() != 16 { - return Err(CryptoError::InvalidKeySize); - } - - let ciphertext = ciphertext.as_ref(); - if ciphertext.len() < 16 { - return Err(CryptoError::DecryptionFailed); - } + ensure_key_size(key, 16)?; // Extract IV from the beginning - let iv = &ciphertext[..16]; - let encrypted_data = &ciphertext[16..]; + let (iv, encrypted_data) = split_iv(ciphertext.as_ref())?; // Decrypt with the extracted IV self.decrypt_with_iv(key, iv, encrypted_data) diff --git a/keetanetwork-crypto/src/algorithms/mod.rs b/keetanetwork-crypto/src/algorithms/mod.rs index 651406a..86f97a3 100644 --- a/keetanetwork-crypto/src/algorithms/mod.rs +++ b/keetanetwork-crypto/src/algorithms/mod.rs @@ -27,6 +27,8 @@ pub mod secp256r1; #[cfg(feature = "encryption")] pub mod aes_cbc; #[cfg(feature = "encryption")] +pub(crate) mod aes_common; +#[cfg(feature = "encryption")] pub mod aes_ctr; #[cfg(feature = "encryption")] pub mod aes_gcm;