adaptive_pipeline/infrastructure/adapters/
encryption.rs

1// /////////////////////////////////////////////////////////////////////////////
2// Adaptive Pipeline
3// Copyright (c) 2025 Michael Gardner, A Bit of Help, Inc.
4// SPDX-License-Identifier: BSD-3-Clause
5// See LICENSE file in the project root.
6// /////////////////////////////////////////////////////////////////////////////
7
8// Infrastructure module - contains future features not yet fully utilized
9#![allow(dead_code, unused_imports, unused_variables)]
10//! # Encryption Service Implementation
11//!
12//! This module is part of the Infrastructure layer, providing concrete
13//! implementations of domain interfaces (ports).
14//!
15//! This module provides the concrete implementation of the encryption service
16//! interface for the adaptive pipeline system. It implements various encryption
17//! algorithms with secure key management, authenticated encryption, and
18//! comprehensive error handling.
19//!
20//! ## Overview
21//!
22//! The encryption service implementation provides:
23//!
24//! - **Multi-Algorithm Support**: AES-256-GCM, ChaCha20-Poly1305, AES-128-GCM,
25//!   AES-192-GCM
26//! - **Secure Key Management**: Automatic key zeroization and secure memory
27//!   handling
28//! - **Key Derivation**: Argon2, Scrypt, and PBKDF2 key derivation functions
29//! - **Authenticated Encryption**: Built-in integrity verification and
30//!   authentication
31//! - **Parallel Processing**: Multi-threaded encryption for improved
32//!   performance
33//!
34//! ## Architecture
35//!
36//! The implementation follows the infrastructure layer patterns:
37//!
38//! - **Service Implementation**: `MultiAlgoEncryption` implements domain
39//!   interface
40//! - **Algorithm Handlers**: Specialized handlers for each encryption algorithm
41//! - **Key Management**: Secure key generation, derivation, and storage
42//! - **Memory Security**: Automatic zeroization of sensitive data
43//!
44//! ## Security Features
45//!
46//! ### Authenticated Encryption
47//!
48//! All encryption algorithms provide authenticated encryption with associated
49//! data (AEAD):
50//! - **Confidentiality**: Data is encrypted and unreadable without the key
51//! - **Integrity**: Tampering is detected through authentication tags
52//! - **Authentication**: Verifies data origin and prevents forgery
53//!
54//! ### Key Derivation Functions
55//!
56//! Secure key derivation from passwords or key material:
57//! - **Argon2**: Memory-hard function resistant to GPU attacks
58//! - **Scrypt**: Memory-hard function with tunable parameters
59//! - **PBKDF2**: Standard key derivation with configurable iterations
60//!
61//! ### Memory Security
62//!
63//! Sensitive data is protected in memory:
64//! - **Automatic Zeroization**: Keys are securely wiped from memory
65//! - **Secure Storage**: Minimal exposure of sensitive material
66//! - **Drop Safety**: Automatic cleanup on scope exit
67//!
68//! ## Supported Algorithms
69//!
70//! ### AES-256-GCM
71//! - **Key Size**: 256 bits (32 bytes)
72//! - **Nonce Size**: 96 bits (12 bytes)
73//! - **Performance**: Excellent on modern CPUs with AES-NI
74//! - **Security**: Industry standard, FIPS approved
75//!
76//! ### ChaCha20-Poly1305
77//! - **Key Size**: 256 bits (32 bytes)
78//! - **Nonce Size**: 96 bits (12 bytes)
79//! - **Performance**: Consistent across all platforms
80//! - **Security**: Modern stream cipher, constant-time implementation
81//!
82//! ### AES-128-GCM / AES-192-GCM
83//! - **Key Size**: 128/192 bits (16/24 bytes)
84//! - **Nonce Size**: 96 bits (12 bytes)
85//! - **Performance**: Faster than AES-256, still highly secure
86//! - **Security**: Suitable for most applications
87//!
88//! ## Performance Optimizations
89//!
90//! ### Parallel Processing
91//!
92//! The implementation uses Rayon for parallel processing:
93//! - **Chunk Parallelization**: Multiple chunks processed simultaneously
94//! - **Key Derivation**: Parallel key derivation where supported
95//! - **Thread Pool Management**: Efficient thread utilization
96//!
97//! ### Hardware Acceleration
98//!
99//! - **AES-NI**: Hardware acceleration for AES algorithms
100//! - **Vectorization**: SIMD instructions for improved performance
101//! - **Constant-Time**: Algorithms resistant to timing attacks
102//!
103//! ## Error Handling
104//!
105//! Comprehensive error handling for:
106//! - **Encryption Failures**: Algorithm-specific error conditions
107//! - **Key Derivation Errors**: Invalid parameters or insufficient entropy
108//! - **Authentication Failures**: Tampering detection during decryption
109//! - **Memory Errors**: Secure memory allocation failures
110//!
111//! ## Integration
112//!
113//! The service integrates with:
114//! - **Domain Layer**: Implements `EncryptionService` trait
115//! - **Security Context**: Access control and security policies
116//! - **Pipeline Processing**: Chunk-based processing workflow
117//! - **Metrics Collection**: Performance monitoring and statistics
118
119use aes_gcm::{AeadInPlace, Aes256Gcm, Key, KeyInit, Nonce};
120use argon2::password_hash::SaltString;
121use argon2::{Argon2, PasswordHasher};
122use base64::engine::general_purpose;
123use base64::Engine as _;
124use chacha20poly1305::{ChaCha20Poly1305, Key as ChaChaKey, Nonce as ChaChaNonce};
125use ring::rand::{SecureRandom, SystemRandom};
126use scrypt::password_hash::SaltString as ScryptSalt;
127use scrypt::Scrypt;
128use std::collections::HashMap;
129use zeroize::Zeroize;
130
131use adaptive_pipeline_domain::entities::{ProcessingContext, SecurityContext};
132use adaptive_pipeline_domain::services::{
133    EncryptionAlgorithm, EncryptionConfig, EncryptionService, KeyDerivationFunction, KeyMaterial,
134};
135use adaptive_pipeline_domain::value_objects::{EncryptionBenchmark, FileChunk};
136use adaptive_pipeline_domain::PipelineError;
137
138// NOTE: Domain traits are now synchronous. This implementation is sync and
139// CPU-bound. For async contexts, wrap this implementation with
140// AsyncEncryptionAdapter.
141
142/// Secure key material that automatically zeros sensitive data on drop
143///
144/// This struct provides secure storage for cryptographic key material with
145/// automatic zeroization when the key goes out of scope. It ensures that
146/// sensitive data is properly cleared from memory to prevent information
147/// leakage.
148///
149/// # Security Features
150///
151/// - **Automatic Zeroization**: Key data is securely wiped when dropped
152/// - **Clone Support**: Allows key material to be safely copied when needed
153/// - **Memory Protection**: Minimizes exposure of sensitive data
154///
155/// # Examples
156#[derive(Clone)]
157#[allow(dead_code)]
158struct SecureKey {
159    data: Vec<u8>,
160}
161
162impl SecureKey {
163    fn new(data: Vec<u8>) -> Self {
164        Self { data }
165    }
166
167    fn as_slice(&self) -> &[u8] {
168        &self.data
169    }
170}
171
172/// Concrete implementation of the encryption service
173pub struct MultiAlgoEncryption {
174    rng: SystemRandom,
175    key_cache: HashMap<String, SecureKey>,
176}
177
178impl Default for MultiAlgoEncryption {
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl MultiAlgoEncryption {
185    pub fn new() -> Self {
186        Self {
187            rng: SystemRandom::new(),
188            key_cache: HashMap::new(),
189        }
190    }
191
192    /// Generates a secure random key of the specified length
193    fn generate_key(&self, length: usize) -> Result<Vec<u8>, PipelineError> {
194        let mut key = vec![0u8; length];
195        self.rng
196            .fill(&mut key)
197            .map_err(|e| PipelineError::EncryptionError(format!("Failed to generate key: {:?}", e)))?;
198        Ok(key)
199    }
200
201    /// Generates a secure random nonce/IV
202    fn generate_nonce(&self, length: usize) -> Result<Vec<u8>, PipelineError> {
203        let mut nonce = vec![0u8; length];
204        self.rng
205            .fill(&mut nonce)
206            .map_err(|e| PipelineError::EncryptionError(format!("Failed to generate nonce: {:?}", e)))?;
207        Ok(nonce)
208    }
209
210    /// Derives a key using Argon2
211    fn derive_key_argon2(&self, password: &[u8], salt: &[u8], key_length: usize) -> Result<Vec<u8>, PipelineError> {
212        let argon2 = Argon2::default();
213        let salt_string =
214            SaltString::encode_b64(salt).map_err(|e| PipelineError::EncryptionError(format!("Invalid salt: {}", e)))?;
215
216        let password_hash = argon2
217            .hash_password(password, &salt_string)
218            .map_err(|e| PipelineError::EncryptionError(format!("Argon2 key derivation failed: {}", e)))?;
219
220        let hash_string = password_hash
221            .hash
222            .ok_or_else(|| PipelineError::EncryptionError("Password hash missing".to_string()))?;
223        let hash_bytes = hash_string.as_bytes();
224        if hash_bytes.len() >= key_length {
225            Ok(hash_bytes[..key_length].to_vec())
226        } else {
227            Err(PipelineError::EncryptionError("Derived key too short".to_string()))
228        }
229    }
230
231    /// Derives a key using scrypt
232    fn derive_key_scrypt(&self, password: &[u8], salt: &[u8], key_length: usize) -> Result<Vec<u8>, PipelineError> {
233        let scrypt = Scrypt;
234        let salt_string =
235            ScryptSalt::encode_b64(salt).map_err(|e| PipelineError::EncryptionError(format!("Invalid salt: {}", e)))?;
236
237        let password_hash = scrypt
238            .hash_password(password, &salt_string)
239            .map_err(|e| PipelineError::EncryptionError(format!("Scrypt key derivation failed: {}", e)))?;
240
241        let hash_string = password_hash
242            .hash
243            .ok_or_else(|| PipelineError::EncryptionError("Password hash missing".to_string()))?;
244        let hash_bytes = hash_string.as_bytes();
245        if hash_bytes.len() >= key_length {
246            Ok(hash_bytes[..key_length].to_vec())
247        } else {
248            Err(PipelineError::EncryptionError("Derived key too short".to_string()))
249        }
250    }
251
252    /// Derives a key using PBKDF2 with SHA-256
253    fn derive_key_pbkdf2(
254        &self,
255        password: &[u8],
256        salt: &[u8],
257        iterations: u32,
258        key_length: usize,
259    ) -> Result<Vec<u8>, PipelineError> {
260        let mut key = vec![0u8; key_length];
261        ring::pbkdf2::derive(
262            ring::pbkdf2::PBKDF2_HMAC_SHA256,
263            std::num::NonZeroU32::new(iterations)
264                .ok_or_else(|| PipelineError::EncryptionError("Invalid iteration count".to_string()))?,
265            salt,
266            password,
267            &mut key,
268        );
269        Ok(key)
270    }
271
272    /// Encrypts data using AES-256-GCM
273    fn encrypt_aes256_gcm(&self, data: &[u8], key: &[u8], nonce: &[u8]) -> Result<Vec<u8>, PipelineError> {
274        if key.len() != 32 {
275            return Err(PipelineError::EncryptionError(
276                "AES-256 requires 32-byte key".to_string(),
277            ));
278        }
279        if nonce.len() != 12 {
280            return Err(PipelineError::EncryptionError(
281                "AES-GCM requires 12-byte nonce".to_string(),
282            ));
283        }
284
285        let cipher_key = Key::<Aes256Gcm>::from_slice(key);
286        let cipher = Aes256Gcm::new(cipher_key);
287        let nonce_array = Nonce::from_slice(nonce);
288
289        let mut buffer = data.to_vec();
290        cipher
291            .encrypt_in_place(nonce_array, b"", &mut buffer)
292            .map_err(|e| PipelineError::EncryptionError(format!("AES-256-GCM encryption failed: {:?}", e)))?;
293
294        // Prepend nonce to encrypted data
295        let mut result = nonce.to_vec();
296        result.extend_from_slice(&buffer);
297        Ok(result)
298    }
299
300    /// Decrypts data using AES-256-GCM
301    fn decrypt_aes256_gcm(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>, PipelineError> {
302        if key.len() != 32 {
303            return Err(PipelineError::EncryptionError(
304                "AES-256 requires 32-byte key".to_string(),
305            ));
306        }
307        if data.len() < 12 {
308            return Err(PipelineError::EncryptionError("Encrypted data too short".to_string()));
309        }
310
311        let (nonce, ciphertext) = data.split_at(12);
312        let cipher_key = Key::<Aes256Gcm>::from_slice(key);
313        let cipher = Aes256Gcm::new(cipher_key);
314        let nonce_array = Nonce::from_slice(nonce);
315
316        let mut buffer = ciphertext.to_vec();
317        cipher
318            .decrypt_in_place(nonce_array, b"", &mut buffer)
319            .map_err(|e| PipelineError::EncryptionError(format!("AES-256-GCM decryption failed: {:?}", e)))?;
320
321        Ok(buffer)
322    }
323
324    /// Encrypts data using ChaCha20-Poly1305
325    fn encrypt_chacha20_poly1305(&self, data: &[u8], key: &[u8], nonce: &[u8]) -> Result<Vec<u8>, PipelineError> {
326        if key.len() != 32 {
327            return Err(PipelineError::EncryptionError(
328                "ChaCha20 requires 32-byte key".to_string(),
329            ));
330        }
331        if nonce.len() != 12 {
332            return Err(PipelineError::EncryptionError(
333                "ChaCha20-Poly1305 requires 12-byte nonce".to_string(),
334            ));
335        }
336
337        let cipher_key = ChaChaKey::from_slice(key);
338        let cipher = ChaCha20Poly1305::new(cipher_key);
339        let nonce_array = ChaChaNonce::from_slice(nonce);
340
341        let mut buffer = data.to_vec();
342        cipher
343            .encrypt_in_place(nonce_array, b"", &mut buffer)
344            .map_err(|e| PipelineError::EncryptionError(format!("ChaCha20-Poly1305 encryption failed: {:?}", e)))?;
345
346        // Prepend nonce to encrypted data
347        let mut result = nonce.to_vec();
348        result.extend_from_slice(&buffer);
349        Ok(result)
350    }
351
352    /// Decrypts data using ChaCha20-Poly1305
353    fn decrypt_chacha20_poly1305(&self, data: &[u8], key: &[u8]) -> Result<Vec<u8>, PipelineError> {
354        if key.len() != 32 {
355            return Err(PipelineError::EncryptionError(
356                "ChaCha20 requires 32-byte key".to_string(),
357            ));
358        }
359        if data.len() < 12 {
360            return Err(PipelineError::EncryptionError("Encrypted data too short".to_string()));
361        }
362
363        let (nonce, ciphertext) = data.split_at(12);
364        let cipher_key = ChaChaKey::from_slice(key);
365        let cipher = ChaCha20Poly1305::new(cipher_key);
366        let nonce_array = ChaChaNonce::from_slice(nonce);
367
368        let mut buffer = ciphertext.to_vec();
369        cipher
370            .decrypt_in_place(nonce_array, b"", &mut buffer)
371            .map_err(|e| PipelineError::EncryptionError(format!("ChaCha20-Poly1305 decryption failed: {:?}", e)))?;
372
373        Ok(buffer)
374    }
375
376    /// Calculates SHA-256 hash for integrity verification
377    fn calculate_hash(&self, data: &[u8]) -> Vec<u8> {
378        ring::digest::digest(&ring::digest::SHA256, data).as_ref().to_vec()
379    }
380}
381
382impl EncryptionService for MultiAlgoEncryption {
383    fn encrypt_chunk(
384        &self,
385        chunk: FileChunk,
386        config: &EncryptionConfig,
387        key_material: &KeyMaterial,
388        context: &mut ProcessingContext,
389    ) -> Result<FileChunk, PipelineError> {
390        let data = chunk.data().to_vec();
391
392        // Use the provided key material
393        let key = key_material;
394
395        // Generate nonce
396        let nonce = self.generate_nonce(12)?; // 12 bytes for GCM/ChaCha20-Poly1305
397
398        // Encrypt based on algorithm
399        let encrypted_data = match &config.algorithm {
400            EncryptionAlgorithm::Aes256Gcm => self.encrypt_aes256_gcm(&data, &key.key, &nonce)?,
401            EncryptionAlgorithm::ChaCha20Poly1305 => self.encrypt_chacha20_poly1305(&data, &key.key, &nonce)?,
402            EncryptionAlgorithm::Aes128Gcm => {
403                if key.len() != 16 {
404                    return Err(PipelineError::EncryptionError(
405                        "AES-128 requires 16-byte key".to_string(),
406                    ));
407                }
408                // Similar implementation for AES-128
409                return Err(PipelineError::EncryptionError(
410                    "AES-128-GCM not yet fully implemented".to_string(),
411                ));
412            }
413            EncryptionAlgorithm::Aes192Gcm => {
414                if key.len() != 24 {
415                    return Err(PipelineError::EncryptionError(
416                        "AES-192 requires 24-byte key".to_string(),
417                    ));
418                }
419                // Similar implementation for AES-192
420                return Err(PipelineError::EncryptionError(
421                    "AES-192-GCM not yet fully implemented".to_string(),
422                ));
423            }
424            EncryptionAlgorithm::Custom(name) => {
425                return Err(PipelineError::EncryptionError(format!(
426                    "Custom algorithm '{}' not implemented",
427                    name
428                )));
429            }
430        };
431
432        // Create new chunk with encrypted data
433        let chunk = chunk.with_data(encrypted_data)?;
434
435        // Calculate integrity hash
436        let integrity_hash = self.calculate_hash(chunk.data());
437
438        // Update context with encryption metadata
439        context.add_metadata("encryption_algorithm".to_string(), config.algorithm.to_string());
440        context.add_metadata("integrity_hash".to_string(), hex::encode(&integrity_hash));
441        context.add_metadata("encrypted".to_string(), "true".to_string());
442
443        Ok(chunk)
444    }
445
446    fn decrypt_chunk(
447        &self,
448        chunk: FileChunk,
449        config: &EncryptionConfig,
450        key_material: &KeyMaterial,
451        context: &mut ProcessingContext,
452    ) -> Result<FileChunk, PipelineError> {
453        let data = chunk.data().to_vec();
454
455        // Use the provided key material
456        let key = key_material;
457
458        // Decrypt based on algorithm
459        let decrypted_data = match &config.algorithm {
460            EncryptionAlgorithm::Aes256Gcm => self.decrypt_aes256_gcm(&data, &key.key)?,
461            EncryptionAlgorithm::ChaCha20Poly1305 => self.decrypt_chacha20_poly1305(&data, &key.key)?,
462            EncryptionAlgorithm::Aes128Gcm => {
463                return Err(PipelineError::EncryptionError(
464                    "AES-128-GCM not yet fully implemented".to_string(),
465                ));
466            }
467            EncryptionAlgorithm::Aes192Gcm => {
468                return Err(PipelineError::EncryptionError(
469                    "AES-192-GCM not yet fully implemented".to_string(),
470                ));
471            }
472            EncryptionAlgorithm::Custom(name) => {
473                return Err(PipelineError::EncryptionError(format!(
474                    "Custom algorithm '{}' not implemented",
475                    name
476                )));
477            }
478        };
479
480        // Create new chunk with decrypted data
481        let chunk = chunk.with_data(decrypted_data)?;
482
483        // Verify integrity if hash is available
484        if let Some(expected_hash) = context.get_metadata("integrity_hash") {
485            let actual_hash = hex::encode(self.calculate_hash(chunk.data()));
486            if actual_hash != *expected_hash {
487                return Err(PipelineError::EncryptionError(
488                    "Integrity verification failed".to_string(),
489                ));
490            }
491        }
492
493        // Update context
494        context.add_metadata("decryption_algorithm".to_string(), config.algorithm.to_string());
495        context.add_metadata("encrypted".to_string(), "false".to_string());
496
497        Ok(chunk)
498    }
499
500    fn derive_key_material(
501        &self,
502        password: &str,
503        config: &EncryptionConfig,
504        security_context: &SecurityContext,
505    ) -> Result<KeyMaterial, PipelineError> {
506        let password_bytes = password.as_bytes();
507        let salt = self.generate_nonce(32)?; // 32-byte salt
508
509        let key_length = match &config.algorithm {
510            EncryptionAlgorithm::Aes128Gcm => 16,
511            EncryptionAlgorithm::Aes192Gcm => 24,
512            EncryptionAlgorithm::Aes256Gcm => 32,
513            EncryptionAlgorithm::ChaCha20Poly1305 => 32,
514            EncryptionAlgorithm::Custom(_) => 32, // Default to 32 bytes for custom algorithms
515        };
516
517        let kdf = &config.key_derivation;
518
519        let key_bytes = match kdf {
520            KeyDerivationFunction::Argon2 => self.derive_key_argon2(password_bytes, &salt, key_length)?,
521            KeyDerivationFunction::Scrypt => self.derive_key_scrypt(password_bytes, &salt, key_length)?,
522            KeyDerivationFunction::Pbkdf2 => {
523                self.derive_key_pbkdf2(password_bytes, &salt, config.iterations, key_length)?
524            }
525            KeyDerivationFunction::Custom(name) => {
526                return Err(PipelineError::EncryptionError(format!(
527                    "Custom KDF '{}' not implemented",
528                    name
529                )));
530            }
531        };
532
533        let nonce = self.generate_nonce(12)?; // 12 bytes for GCM
534
535        Ok(KeyMaterial::new(key_bytes, nonce, salt, config.algorithm.clone()))
536    }
537
538    fn generate_key_material(
539        &self,
540        config: &EncryptionConfig,
541        security_context: &SecurityContext,
542    ) -> Result<KeyMaterial, PipelineError> {
543        let key_length = match &config.algorithm {
544            EncryptionAlgorithm::Aes128Gcm => 16,
545            EncryptionAlgorithm::Aes192Gcm => 24,
546            EncryptionAlgorithm::Aes256Gcm => 32,
547            EncryptionAlgorithm::ChaCha20Poly1305 => 32,
548            EncryptionAlgorithm::Custom(_) => 32, // Default to 32 bytes for custom algorithms
549        };
550
551        let key = self.generate_key(key_length)?;
552        let nonce = self.generate_nonce(12)?;
553        let salt = self.generate_nonce(32)?;
554
555        Ok(KeyMaterial::new(key, nonce, salt, config.algorithm.clone()))
556    }
557
558    fn validate_config(&self, config: &EncryptionConfig) -> Result<(), PipelineError> {
559        // Validate algorithm
560        match &config.algorithm {
561            EncryptionAlgorithm::Aes128Gcm
562            | EncryptionAlgorithm::Aes192Gcm
563            | EncryptionAlgorithm::Aes256Gcm
564            | EncryptionAlgorithm::ChaCha20Poly1305 => {
565                // These are supported
566            }
567            EncryptionAlgorithm::Custom(name) => {
568                return Err(PipelineError::InvalidConfiguration(format!(
569                    "Custom algorithm '{}' not supported",
570                    name
571                )));
572            }
573        }
574
575        // Validate key derivation function
576        match &config.key_derivation {
577            KeyDerivationFunction::Argon2 => {}
578            KeyDerivationFunction::Scrypt => {}
579            KeyDerivationFunction::Pbkdf2 => {
580                if config.iterations < 10_000 {
581                    return Err(PipelineError::InvalidConfiguration(
582                        "PBKDF2 iterations should be at least 10,000".to_string(),
583                    ));
584                }
585            }
586            KeyDerivationFunction::Custom(_) => {}
587        }
588
589        Ok(())
590    }
591
592    fn benchmark_algorithm(
593        &self,
594        algorithm: &EncryptionAlgorithm,
595        test_data: &[u8],
596    ) -> Result<EncryptionBenchmark, PipelineError> {
597        let key_length = match algorithm {
598            EncryptionAlgorithm::Aes128Gcm => 16,
599            EncryptionAlgorithm::Aes192Gcm => 24,
600            EncryptionAlgorithm::Aes256Gcm => 32,
601            EncryptionAlgorithm::ChaCha20Poly1305 => 32,
602            EncryptionAlgorithm::Custom(_) => 32, // Default to 32 bytes for custom algorithms
603        };
604        let key = self.generate_key(key_length)?;
605        let nonce = self.generate_nonce(12)?;
606
607        let start = std::time::Instant::now();
608
609        // Encrypt the data
610        let encrypted = match algorithm {
611            EncryptionAlgorithm::Aes256Gcm => self.encrypt_aes256_gcm(test_data, &key, &nonce)?,
612            EncryptionAlgorithm::ChaCha20Poly1305 => self.encrypt_chacha20_poly1305(test_data, &key, &nonce)?,
613            _ => {
614                return Err(PipelineError::EncryptionError(
615                    "Algorithm not supported for benchmarking".to_string(),
616                ));
617            }
618        };
619
620        let encryption_time = start.elapsed();
621
622        // Benchmark decryption
623        let start = std::time::Instant::now();
624        let _decrypted = match algorithm {
625            EncryptionAlgorithm::Aes256Gcm => self.decrypt_aes256_gcm(&encrypted, &key)?,
626            EncryptionAlgorithm::ChaCha20Poly1305 => self.decrypt_chacha20_poly1305(&encrypted, &key)?,
627            _ => {
628                return Err(PipelineError::EncryptionError(
629                    "Algorithm not supported for benchmarking".to_string(),
630                ));
631            }
632        };
633        let decryption_time = start.elapsed();
634
635        // Calculate speeds in MB/s
636        let data_size_mb = (test_data.len() as f64) / (1024.0 * 1024.0);
637        let encryption_speed = data_size_mb / encryption_time.as_secs_f64();
638        let _decryption_speed = data_size_mb / decryption_time.as_secs_f64();
639
640        Ok(EncryptionBenchmark::new(
641            algorithm.clone(),
642            encryption_speed,
643            encryption_time,
644            32.0, // Estimated memory usage
645            70.0, // Estimated CPU usage
646            data_size_mb,
647        ))
648    }
649
650    fn wipe_key_material(&self, key_material: &mut KeyMaterial) -> Result<(), PipelineError> {
651        // Zero out the key material
652        key_material.zeroize();
653        Ok(())
654    }
655
656    fn store_key_material(
657        &self,
658        key_material: &KeyMaterial,
659        key_id: &str,
660        security_context: &SecurityContext,
661    ) -> Result<(), PipelineError> {
662        // In a real implementation, this would store in HSM or secure key vault
663        // For now, just validate inputs
664        if key_id.is_empty() {
665            return Err(PipelineError::EncryptionError("Key ID cannot be empty".to_string()));
666        }
667
668        if key_material.key.is_empty() {
669            return Err(PipelineError::EncryptionError(
670                "Key material cannot be empty".to_string(),
671            ));
672        }
673
674        // TODO: Implement actual secure storage
675        Ok(())
676    }
677
678    fn retrieve_key_material(
679        &self,
680        key_id: &str,
681        security_context: &SecurityContext,
682    ) -> Result<KeyMaterial, PipelineError> {
683        // In a real implementation, this would retrieve from HSM or secure key vault
684        Err(PipelineError::EncryptionError(
685            "Key retrieval not yet implemented".to_string(),
686        ))
687    }
688
689    fn rotate_keys(
690        &self,
691        old_key_id: &str,
692        new_config: &EncryptionConfig,
693        security_context: &SecurityContext,
694    ) -> Result<String, PipelineError> {
695        // In a real implementation, this would generate new keys and update storage
696        Err(PipelineError::EncryptionError(
697            "Key rotation not yet implemented".to_string(),
698        ))
699    }
700
701    fn supported_algorithms(&self) -> Vec<EncryptionAlgorithm> {
702        vec![
703            EncryptionAlgorithm::Aes256Gcm,
704            EncryptionAlgorithm::ChaCha20Poly1305,
705            EncryptionAlgorithm::Aes128Gcm,
706        ]
707    }
708}
709
710// Implement StageService trait for unified interface
711impl adaptive_pipeline_domain::services::StageService for MultiAlgoEncryption {
712    fn process_chunk(
713        &self,
714        chunk: adaptive_pipeline_domain::FileChunk,
715        config: &adaptive_pipeline_domain::entities::StageConfiguration,
716        context: &mut adaptive_pipeline_domain::ProcessingContext,
717    ) -> Result<adaptive_pipeline_domain::FileChunk, adaptive_pipeline_domain::PipelineError> {
718        use adaptive_pipeline_domain::services::FromParameters;
719
720        // Type-safe extraction of EncryptionConfig from parameters
721        let encryption_config = EncryptionConfig::from_parameters(&config.parameters)?;
722
723        // Extract KeyMaterial from parameters
724        // Expected format: base64-encoded key, nonce, salt
725        let key_b64 = config
726            .parameters
727            .get("key")
728            .ok_or_else(|| adaptive_pipeline_domain::PipelineError::MissingParameter("key".into()))?;
729
730        let nonce_b64 = config
731            .parameters
732            .get("nonce")
733            .ok_or_else(|| adaptive_pipeline_domain::PipelineError::MissingParameter("nonce".into()))?;
734
735        let salt_b64 = config
736            .parameters
737            .get("salt")
738            .ok_or_else(|| adaptive_pipeline_domain::PipelineError::MissingParameter("salt".into()))?;
739
740        // Decode base64 strings using Engine API
741        let key = general_purpose::STANDARD.decode(key_b64).map_err(|e| {
742            adaptive_pipeline_domain::PipelineError::InvalidParameter(format!("Invalid base64 key: {}", e))
743        })?;
744
745        let nonce = general_purpose::STANDARD.decode(nonce_b64).map_err(|e| {
746            adaptive_pipeline_domain::PipelineError::InvalidParameter(format!("Invalid base64 nonce: {}", e))
747        })?;
748
749        let salt = general_purpose::STANDARD.decode(salt_b64).map_err(|e| {
750            adaptive_pipeline_domain::PipelineError::InvalidParameter(format!("Invalid base64 salt: {}", e))
751        })?;
752
753        let key_material = KeyMaterial::new(key, nonce, salt, encryption_config.algorithm.clone());
754
755        match config.operation {
756            adaptive_pipeline_domain::entities::Operation::Forward => {
757                self.encrypt_chunk(chunk, &encryption_config, &key_material, context)
758            }
759            adaptive_pipeline_domain::entities::Operation::Reverse => {
760                self.decrypt_chunk(chunk, &encryption_config, &key_material, context)
761            }
762        }
763    }
764
765    fn position(&self) -> adaptive_pipeline_domain::entities::StagePosition {
766        adaptive_pipeline_domain::entities::StagePosition::PreBinary
767    }
768
769    fn is_reversible(&self) -> bool {
770        true
771    }
772
773    fn stage_type(&self) -> adaptive_pipeline_domain::entities::StageType {
774        adaptive_pipeline_domain::entities::StageType::Encryption
775    }
776}