Skip to main content

mabi_opcua/security/
crypto.rs

1//! Cryptographic operations for OPC UA security.
2//!
3//! Provides encryption, decryption, signing, and hashing operations
4//! using pluggable algorithm implementations.
5
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use tracing::{debug, trace};
9
10use super::policy::SecurityPolicyConfig;
11use crate::config::SecurityPolicy;
12
13/// Cryptographic error types.
14#[derive(Debug, Error)]
15pub enum CryptoError {
16    #[error("Encryption failed: {0}")]
17    EncryptionFailed(String),
18
19    #[error("Decryption failed: {0}")]
20    DecryptionFailed(String),
21
22    #[error("Signing failed: {0}")]
23    SigningFailed(String),
24
25    #[error("Verification failed: {0}")]
26    VerificationFailed(String),
27
28    #[error("Invalid key: {0}")]
29    InvalidKey(String),
30
31    #[error("Invalid data: {0}")]
32    InvalidData(String),
33
34    #[error("Unsupported algorithm: {0}")]
35    UnsupportedAlgorithm(String),
36
37    #[error("Key generation failed: {0}")]
38    KeyGenerationFailed(String),
39}
40
41/// Result type for cryptographic operations.
42pub type CryptoResult<T> = Result<T, CryptoError>;
43
44/// Symmetric encryption algorithm identifiers.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46pub enum SymmetricAlgorithm {
47    /// No encryption.
48    None,
49    /// AES-128-CBC.
50    Aes128Cbc,
51    /// AES-256-CBC.
52    Aes256Cbc,
53}
54
55impl SymmetricAlgorithm {
56    /// Get the key length in bytes.
57    pub fn key_length(&self) -> usize {
58        match self {
59            Self::None => 0,
60            Self::Aes128Cbc => 16,
61            Self::Aes256Cbc => 32,
62        }
63    }
64
65    /// Get the block size in bytes.
66    pub fn block_size(&self) -> usize {
67        match self {
68            Self::None => 0,
69            Self::Aes128Cbc | Self::Aes256Cbc => 16,
70        }
71    }
72
73    /// Get the IV length in bytes.
74    pub fn iv_length(&self) -> usize {
75        self.block_size()
76    }
77}
78
79/// Asymmetric encryption algorithm identifiers.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81pub enum AsymmetricAlgorithm {
82    /// No encryption.
83    None,
84    /// RSA PKCS#1 v1.5.
85    RsaPkcs1,
86    /// RSA OAEP with SHA-1.
87    RsaOaepSha1,
88    /// RSA OAEP with SHA-256.
89    RsaOaepSha256,
90    /// RSA PSS with SHA-256.
91    RsaPssSha256,
92}
93
94/// Hash algorithm identifiers.
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
96pub enum HashAlgorithm {
97    /// SHA-1 (deprecated).
98    Sha1,
99    /// SHA-256.
100    Sha256,
101    /// SHA-384.
102    Sha384,
103    /// SHA-512.
104    Sha512,
105}
106
107impl HashAlgorithm {
108    /// Get the output length in bytes.
109    pub fn output_length(&self) -> usize {
110        match self {
111            Self::Sha1 => 20,
112            Self::Sha256 => 32,
113            Self::Sha384 => 48,
114            Self::Sha512 => 64,
115        }
116    }
117}
118
119/// Result of an encryption operation.
120#[derive(Debug, Clone)]
121pub struct EncryptionResult {
122    /// Encrypted data.
123    pub ciphertext: Vec<u8>,
124    /// Initialization vector (for symmetric encryption).
125    pub iv: Option<Vec<u8>>,
126}
127
128/// Result of a decryption operation.
129#[derive(Debug, Clone)]
130pub struct DecryptionResult {
131    /// Decrypted plaintext.
132    pub plaintext: Vec<u8>,
133}
134
135/// Result of a signing operation.
136#[derive(Debug, Clone)]
137pub struct SignatureResult {
138    /// Digital signature.
139    pub signature: Vec<u8>,
140}
141
142/// Crypto provider configuration.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct CryptoProviderConfig {
145    /// Enable hardware acceleration if available.
146    pub enable_hw_acceleration: bool,
147    /// Random number generator seed (for testing).
148    pub rng_seed: Option<u64>,
149}
150
151impl Default for CryptoProviderConfig {
152    fn default() -> Self {
153        Self {
154            enable_hw_acceleration: true,
155            rng_seed: None,
156        }
157    }
158}
159
160/// Key material for cryptographic operations.
161#[derive(Debug, Clone)]
162pub struct KeyMaterial {
163    /// Signing key.
164    pub signing_key: Vec<u8>,
165    /// Encryption key.
166    pub encrypting_key: Vec<u8>,
167    /// Initialization vector.
168    pub iv: Vec<u8>,
169}
170
171impl KeyMaterial {
172    /// Create empty key material.
173    pub fn empty() -> Self {
174        Self {
175            signing_key: Vec::new(),
176            encrypting_key: Vec::new(),
177            iv: Vec::new(),
178        }
179    }
180
181    /// Check if key material is valid for the given policy.
182    pub fn is_valid_for(&self, policy: &SecurityPolicyConfig) -> bool {
183        if policy.policy == SecurityPolicy::None {
184            return true;
185        }
186
187        self.signing_key.len() >= policy.derived_signature_key_length as usize
188            && self.encrypting_key.len() >= policy.symmetric_key_length as usize
189            && self.iv.len() >= policy.symmetric_block_size as usize
190    }
191}
192
193/// Cryptographic provider for OPC UA security operations.
194///
195/// This is a pluggable interface that can be backed by different
196/// cryptographic libraries (ring, openssl, etc.).
197pub struct CryptoProvider {
198    config: CryptoProviderConfig,
199    policy_config: SecurityPolicyConfig,
200}
201
202impl CryptoProvider {
203    /// Create a new crypto provider with default config.
204    pub fn new(policy: SecurityPolicy) -> Self {
205        Self {
206            config: CryptoProviderConfig::default(),
207            policy_config: SecurityPolicyConfig::for_policy(policy),
208        }
209    }
210
211    /// Create with custom config.
212    pub fn with_config(policy: SecurityPolicy, config: CryptoProviderConfig) -> Self {
213        Self {
214            config,
215            policy_config: SecurityPolicyConfig::for_policy(policy),
216        }
217    }
218
219    /// Get the security policy.
220    pub fn policy(&self) -> SecurityPolicy {
221        self.policy_config.policy
222    }
223
224    /// Get the policy configuration.
225    pub fn policy_config(&self) -> &SecurityPolicyConfig {
226        &self.policy_config
227    }
228
229    // =========================================================================
230    // Symmetric Operations
231    // =========================================================================
232
233    /// Encrypt data using symmetric encryption.
234    pub fn symmetric_encrypt(
235        &self,
236        plaintext: &[u8],
237        key: &[u8],
238        iv: &[u8],
239    ) -> CryptoResult<EncryptionResult> {
240        if self.policy_config.policy == SecurityPolicy::None {
241            return Ok(EncryptionResult {
242                ciphertext: plaintext.to_vec(),
243                iv: None,
244            });
245        }
246
247        // Validate key and IV lengths
248        let expected_key_len = self.policy_config.symmetric_key_length as usize;
249        let expected_iv_len = self.policy_config.symmetric_block_size as usize;
250
251        if key.len() != expected_key_len {
252            return Err(CryptoError::InvalidKey(format!(
253                "Expected key length {}, got {}",
254                expected_key_len,
255                key.len()
256            )));
257        }
258
259        if iv.len() != expected_iv_len {
260            return Err(CryptoError::InvalidData(format!(
261                "Expected IV length {}, got {}",
262                expected_iv_len,
263                iv.len()
264            )));
265        }
266
267        // Apply PKCS7 padding
268        let block_size = self.policy_config.symmetric_block_size as usize;
269        let padded = pkcs7_pad(plaintext, block_size);
270
271        // Simulate AES-CBC encryption
272        // In production, this would use a real crypto library
273        let ciphertext = simulate_aes_cbc_encrypt(&padded, key, iv);
274
275        trace!(
276            plaintext_len = plaintext.len(),
277            ciphertext_len = ciphertext.len(),
278            "Symmetric encryption completed"
279        );
280
281        Ok(EncryptionResult {
282            ciphertext,
283            iv: Some(iv.to_vec()),
284        })
285    }
286
287    /// Decrypt data using symmetric encryption.
288    pub fn symmetric_decrypt(
289        &self,
290        ciphertext: &[u8],
291        key: &[u8],
292        iv: &[u8],
293    ) -> CryptoResult<DecryptionResult> {
294        if self.policy_config.policy == SecurityPolicy::None {
295            return Ok(DecryptionResult {
296                plaintext: ciphertext.to_vec(),
297            });
298        }
299
300        // Validate inputs
301        let block_size = self.policy_config.symmetric_block_size as usize;
302        if ciphertext.len() % block_size != 0 {
303            return Err(CryptoError::InvalidData(
304                "Ciphertext length must be multiple of block size".to_string(),
305            ));
306        }
307
308        // Simulate AES-CBC decryption
309        let decrypted = simulate_aes_cbc_decrypt(ciphertext, key, iv);
310
311        // Remove PKCS7 padding
312        let plaintext = pkcs7_unpad(&decrypted).map_err(|e| CryptoError::DecryptionFailed(e))?;
313
314        trace!(
315            ciphertext_len = ciphertext.len(),
316            plaintext_len = plaintext.len(),
317            "Symmetric decryption completed"
318        );
319
320        Ok(DecryptionResult { plaintext })
321    }
322
323    // =========================================================================
324    // Asymmetric Operations
325    // =========================================================================
326
327    /// Encrypt data using asymmetric encryption (RSA).
328    pub fn asymmetric_encrypt(
329        &self,
330        plaintext: &[u8],
331        public_key: &[u8],
332    ) -> CryptoResult<EncryptionResult> {
333        if self.policy_config.policy == SecurityPolicy::None {
334            return Ok(EncryptionResult {
335                ciphertext: plaintext.to_vec(),
336                iv: None,
337            });
338        }
339
340        // Validate public key
341        if public_key.is_empty() {
342            return Err(CryptoError::InvalidKey("Empty public key".to_string()));
343        }
344
345        // Simulate RSA encryption
346        // In production, would use actual RSA implementation
347        let ciphertext = simulate_rsa_encrypt(plaintext, public_key);
348
349        debug!(
350            plaintext_len = plaintext.len(),
351            ciphertext_len = ciphertext.len(),
352            "Asymmetric encryption completed"
353        );
354
355        Ok(EncryptionResult {
356            ciphertext,
357            iv: None,
358        })
359    }
360
361    /// Decrypt data using asymmetric encryption (RSA).
362    pub fn asymmetric_decrypt(
363        &self,
364        ciphertext: &[u8],
365        private_key: &[u8],
366    ) -> CryptoResult<DecryptionResult> {
367        if self.policy_config.policy == SecurityPolicy::None {
368            return Ok(DecryptionResult {
369                plaintext: ciphertext.to_vec(),
370            });
371        }
372
373        // Validate private key
374        if private_key.is_empty() {
375            return Err(CryptoError::InvalidKey("Empty private key".to_string()));
376        }
377
378        // Simulate RSA decryption
379        let plaintext = simulate_rsa_decrypt(ciphertext, private_key);
380
381        debug!(
382            ciphertext_len = ciphertext.len(),
383            plaintext_len = plaintext.len(),
384            "Asymmetric decryption completed"
385        );
386
387        Ok(DecryptionResult { plaintext })
388    }
389
390    // =========================================================================
391    // Signing Operations
392    // =========================================================================
393
394    /// Sign data using HMAC (symmetric).
395    pub fn hmac_sign(&self, data: &[u8], key: &[u8]) -> CryptoResult<SignatureResult> {
396        if self.policy_config.policy == SecurityPolicy::None {
397            return Ok(SignatureResult {
398                signature: Vec::new(),
399            });
400        }
401
402        // Simulate HMAC-SHA256
403        let signature = simulate_hmac_sha256(data, key);
404
405        trace!(
406            data_len = data.len(),
407            sig_len = signature.len(),
408            "HMAC sign completed"
409        );
410
411        Ok(SignatureResult { signature })
412    }
413
414    /// Verify HMAC signature.
415    pub fn hmac_verify(&self, data: &[u8], key: &[u8], signature: &[u8]) -> CryptoResult<bool> {
416        if self.policy_config.policy == SecurityPolicy::None {
417            return Ok(true);
418        }
419
420        let expected = simulate_hmac_sha256(data, key);
421        let valid = constant_time_compare(&expected, signature);
422
423        trace!(valid, "HMAC verify completed");
424
425        Ok(valid)
426    }
427
428    /// Sign data using RSA.
429    pub fn rsa_sign(&self, data: &[u8], private_key: &[u8]) -> CryptoResult<SignatureResult> {
430        if self.policy_config.policy == SecurityPolicy::None {
431            return Ok(SignatureResult {
432                signature: Vec::new(),
433            });
434        }
435
436        // Simulate RSA signature
437        let signature = simulate_rsa_sign(data, private_key);
438
439        debug!(
440            data_len = data.len(),
441            sig_len = signature.len(),
442            "RSA sign completed"
443        );
444
445        Ok(SignatureResult { signature })
446    }
447
448    /// Verify RSA signature.
449    pub fn rsa_verify(
450        &self,
451        data: &[u8],
452        public_key: &[u8],
453        signature: &[u8],
454    ) -> CryptoResult<bool> {
455        if self.policy_config.policy == SecurityPolicy::None {
456            return Ok(true);
457        }
458
459        // Simulate RSA verification
460        let valid = simulate_rsa_verify(data, public_key, signature);
461
462        debug!(valid, "RSA verify completed");
463
464        Ok(valid)
465    }
466
467    // =========================================================================
468    // Hashing Operations
469    // =========================================================================
470
471    /// Compute hash of data.
472    pub fn hash(&self, algorithm: HashAlgorithm, data: &[u8]) -> Vec<u8> {
473        match algorithm {
474            HashAlgorithm::Sha1 => simulate_sha1(data),
475            HashAlgorithm::Sha256 => simulate_sha256(data),
476            HashAlgorithm::Sha384 => simulate_sha384(data),
477            HashAlgorithm::Sha512 => simulate_sha512(data),
478        }
479    }
480
481    /// Compute SHA-256 hash.
482    pub fn sha256(&self, data: &[u8]) -> Vec<u8> {
483        self.hash(HashAlgorithm::Sha256, data)
484    }
485
486    // =========================================================================
487    // Key Derivation
488    // =========================================================================
489
490    /// Derive keys from shared secret using P_SHA256.
491    pub fn derive_keys(
492        &self,
493        secret: &[u8],
494        seed: &[u8],
495        signing_key_length: usize,
496        encrypting_key_length: usize,
497        iv_length: usize,
498    ) -> CryptoResult<KeyMaterial> {
499        if self.policy_config.policy == SecurityPolicy::None {
500            return Ok(KeyMaterial::empty());
501        }
502
503        // Derive key material using P_SHA256
504        let total_length = signing_key_length + encrypting_key_length + iv_length;
505        let derived = p_sha256(secret, seed, total_length);
506
507        let signing_key = derived[..signing_key_length].to_vec();
508        let encrypting_key =
509            derived[signing_key_length..signing_key_length + encrypting_key_length].to_vec();
510        let iv = derived[signing_key_length + encrypting_key_length..].to_vec();
511
512        debug!(
513            signing_key_len = signing_key.len(),
514            encrypting_key_len = encrypting_key.len(),
515            iv_len = iv.len(),
516            "Key derivation completed"
517        );
518
519        Ok(KeyMaterial {
520            signing_key,
521            encrypting_key,
522            iv,
523        })
524    }
525
526    // =========================================================================
527    // Random Number Generation
528    // =========================================================================
529
530    /// Generate random bytes.
531    pub fn random_bytes(&self, length: usize) -> Vec<u8> {
532        if let Some(seed) = self.config.rng_seed {
533            // Deterministic for testing
534            deterministic_random(seed, length)
535        } else {
536            // True random
537            secure_random(length)
538        }
539    }
540
541    /// Generate a random nonce.
542    pub fn generate_nonce(&self) -> Vec<u8> {
543        self.random_bytes(self.policy_config.secure_channel_nonce_length as usize)
544    }
545}
546
547// =========================================================================
548// Padding Functions
549// =========================================================================
550
551/// Apply PKCS7 padding.
552fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec<u8> {
553    let padding_len = block_size - (data.len() % block_size);
554    let mut padded = data.to_vec();
555    padded.extend(std::iter::repeat(padding_len as u8).take(padding_len));
556    padded
557}
558
559/// Remove PKCS7 padding.
560fn pkcs7_unpad(data: &[u8]) -> Result<Vec<u8>, String> {
561    if data.is_empty() {
562        return Err("Empty data".to_string());
563    }
564
565    let padding_len = *data.last().unwrap() as usize;
566    if padding_len == 0 || padding_len > data.len() || padding_len > 16 {
567        return Err("Invalid padding".to_string());
568    }
569
570    // Verify padding bytes
571    for &byte in &data[data.len() - padding_len..] {
572        if byte != padding_len as u8 {
573            return Err("Invalid padding bytes".to_string());
574        }
575    }
576
577    Ok(data[..data.len() - padding_len].to_vec())
578}
579
580/// Constant-time comparison to prevent timing attacks.
581fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
582    if a.len() != b.len() {
583        return false;
584    }
585
586    let mut result = 0u8;
587    for (x, y) in a.iter().zip(b.iter()) {
588        result |= x ^ y;
589    }
590    result == 0
591}
592
593// =========================================================================
594// Simulation Functions (Replace with real crypto in production)
595// =========================================================================
596
597/// Simulate AES-CBC encryption.
598fn simulate_aes_cbc_encrypt(plaintext: &[u8], key: &[u8], iv: &[u8]) -> Vec<u8> {
599    // XOR-based simulation for structure (NOT SECURE - use real AES in production)
600    let mut ciphertext = Vec::with_capacity(plaintext.len());
601    let block_size = 16;
602
603    let mut prev_block = iv.to_vec();
604    for chunk in plaintext.chunks(block_size) {
605        let mut block = vec![0u8; block_size];
606        for (i, &byte) in chunk.iter().enumerate() {
607            block[i] = byte ^ prev_block[i] ^ key[i % key.len()];
608        }
609        prev_block = block.clone();
610        ciphertext.extend(block);
611    }
612
613    ciphertext
614}
615
616/// Simulate AES-CBC decryption.
617fn simulate_aes_cbc_decrypt(ciphertext: &[u8], key: &[u8], iv: &[u8]) -> Vec<u8> {
618    let mut plaintext = Vec::with_capacity(ciphertext.len());
619    let block_size = 16;
620
621    let mut prev_block = iv.to_vec();
622    for chunk in ciphertext.chunks(block_size) {
623        let mut block = vec![0u8; block_size];
624        for (i, &byte) in chunk.iter().enumerate() {
625            block[i] = byte ^ prev_block[i] ^ key[i % key.len()];
626        }
627        prev_block = chunk.to_vec();
628        plaintext.extend(block);
629    }
630
631    plaintext
632}
633
634/// Simulate RSA encryption.
635fn simulate_rsa_encrypt(plaintext: &[u8], public_key: &[u8]) -> Vec<u8> {
636    // Simple XOR simulation (NOT SECURE)
637    plaintext
638        .iter()
639        .enumerate()
640        .map(|(i, &b)| b ^ public_key[i % public_key.len()])
641        .collect()
642}
643
644/// Simulate RSA decryption.
645fn simulate_rsa_decrypt(ciphertext: &[u8], private_key: &[u8]) -> Vec<u8> {
646    // Simple XOR simulation (NOT SECURE)
647    ciphertext
648        .iter()
649        .enumerate()
650        .map(|(i, &b)| b ^ private_key[i % private_key.len()])
651        .collect()
652}
653
654/// Simulate HMAC-SHA256.
655fn simulate_hmac_sha256(data: &[u8], key: &[u8]) -> Vec<u8> {
656    // Simple hash simulation
657    let mut hash = vec![0u8; 32];
658    for (i, &b) in data.iter().enumerate() {
659        hash[i % 32] ^= b ^ key[i % key.len()];
660    }
661    hash
662}
663
664/// Simulate RSA signing.
665fn simulate_rsa_sign(data: &[u8], private_key: &[u8]) -> Vec<u8> {
666    simulate_hmac_sha256(data, private_key)
667}
668
669/// Simulate RSA verification.
670fn simulate_rsa_verify(data: &[u8], public_key: &[u8], signature: &[u8]) -> bool {
671    let expected = simulate_hmac_sha256(data, public_key);
672    constant_time_compare(&expected, signature)
673}
674
675/// Simulate SHA-1.
676fn simulate_sha1(data: &[u8]) -> Vec<u8> {
677    let mut hash = vec![0u8; 20];
678    for (i, &b) in data.iter().enumerate() {
679        hash[i % 20] = hash[i % 20].wrapping_add(b);
680    }
681    hash
682}
683
684/// Simulate SHA-256.
685fn simulate_sha256(data: &[u8]) -> Vec<u8> {
686    let mut hash = vec![0u8; 32];
687    for (i, &b) in data.iter().enumerate() {
688        hash[i % 32] = hash[i % 32].wrapping_add(b);
689    }
690    hash
691}
692
693/// Simulate SHA-384.
694fn simulate_sha384(data: &[u8]) -> Vec<u8> {
695    let mut hash = vec![0u8; 48];
696    for (i, &b) in data.iter().enumerate() {
697        hash[i % 48] = hash[i % 48].wrapping_add(b);
698    }
699    hash
700}
701
702/// Simulate SHA-512.
703fn simulate_sha512(data: &[u8]) -> Vec<u8> {
704    let mut hash = vec![0u8; 64];
705    for (i, &b) in data.iter().enumerate() {
706        hash[i % 64] = hash[i % 64].wrapping_add(b);
707    }
708    hash
709}
710
711/// P_SHA256 key derivation function.
712fn p_sha256(secret: &[u8], seed: &[u8], length: usize) -> Vec<u8> {
713    let mut result = Vec::with_capacity(length);
714    let mut a = seed.to_vec();
715
716    while result.len() < length {
717        a = simulate_hmac_sha256(&a, secret);
718        let mut combined = a.clone();
719        combined.extend_from_slice(seed);
720        let p = simulate_hmac_sha256(&combined, secret);
721        result.extend(p);
722    }
723
724    result.truncate(length);
725    result
726}
727
728/// Generate deterministic random bytes for testing.
729fn deterministic_random(seed: u64, length: usize) -> Vec<u8> {
730    let mut bytes = Vec::with_capacity(length);
731    let mut state = seed;
732
733    for _ in 0..length {
734        state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
735        bytes.push((state >> 56) as u8);
736    }
737
738    bytes
739}
740
741/// Generate secure random bytes.
742fn secure_random(length: usize) -> Vec<u8> {
743    use std::time::{SystemTime, UNIX_EPOCH};
744
745    let seed = SystemTime::now()
746        .duration_since(UNIX_EPOCH)
747        .unwrap()
748        .as_nanos() as u64;
749
750    deterministic_random(seed, length)
751}
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756
757    #[test]
758    fn test_pkcs7_padding() {
759        let data = b"hello";
760        let padded = pkcs7_pad(data, 16);
761        assert_eq!(padded.len(), 16);
762        assert_eq!(padded[5..], [11u8; 11]);
763
764        let unpadded = pkcs7_unpad(&padded).unwrap();
765        assert_eq!(unpadded, data);
766    }
767
768    #[test]
769    fn test_symmetric_encrypt_decrypt() {
770        let provider = CryptoProvider::new(SecurityPolicy::Basic256Sha256);
771        let key = vec![0u8; 32];
772        let iv = vec![0u8; 16];
773        let plaintext = b"Hello, OPC UA!";
774
775        let encrypted = provider.symmetric_encrypt(plaintext, &key, &iv).unwrap();
776        let decrypted = provider
777            .symmetric_decrypt(&encrypted.ciphertext, &key, &iv)
778            .unwrap();
779
780        assert_eq!(decrypted.plaintext, plaintext);
781    }
782
783    #[test]
784    fn test_none_policy_passthrough() {
785        let provider = CryptoProvider::new(SecurityPolicy::None);
786        let data = b"test data";
787        let key = vec![0u8; 32];
788        let iv = vec![0u8; 16];
789
790        let encrypted = provider.symmetric_encrypt(data, &key, &iv).unwrap();
791        assert_eq!(encrypted.ciphertext, data);
792
793        let decrypted = provider.symmetric_decrypt(data, &key, &iv).unwrap();
794        assert_eq!(decrypted.plaintext, data);
795    }
796
797    #[test]
798    fn test_hmac_sign_verify() {
799        let provider = CryptoProvider::new(SecurityPolicy::Basic256Sha256);
800        let key = vec![0xAB; 32];
801        let data = b"Message to sign";
802
803        let signature = provider.hmac_sign(data, &key).unwrap();
804        let valid = provider
805            .hmac_verify(data, &key, &signature.signature)
806            .unwrap();
807
808        assert!(valid);
809    }
810
811    #[test]
812    fn test_key_derivation() {
813        let provider = CryptoProvider::new(SecurityPolicy::Basic256Sha256);
814        let secret = vec![0x42; 32];
815        let seed = vec![0x13; 32];
816
817        let keys = provider.derive_keys(&secret, &seed, 32, 32, 16).unwrap();
818
819        assert_eq!(keys.signing_key.len(), 32);
820        assert_eq!(keys.encrypting_key.len(), 32);
821        assert_eq!(keys.iv.len(), 16);
822    }
823
824    #[test]
825    fn test_nonce_generation() {
826        let provider = CryptoProvider::new(SecurityPolicy::Basic256Sha256);
827        let nonce = provider.generate_nonce();
828
829        assert_eq!(nonce.len(), 32);
830    }
831
832    #[test]
833    fn test_constant_time_compare() {
834        assert!(constant_time_compare(b"test", b"test"));
835        assert!(!constant_time_compare(b"test", b"Test"));
836        assert!(!constant_time_compare(b"test", b"testing"));
837    }
838}