Skip to main content

exif_oxide/processor_registry/processors/
nikon.rs

1//! Nikon-specific BinaryDataProcessor implementations
2//!
3//! These processors implement the BinaryDataProcessor trait for Nikon camera data
4//! while delegating to the existing Nikon implementation modules. This maintains
5//! the "Trust ExifTool" principle by reusing proven processing logic.
6//!
7//! ## ExifTool Reference
8//!
9//! Nikon.pm ProcessNikonEncrypted and related processing functions
10
11use super::super::{
12    BinaryDataProcessor, ProcessorCapability, ProcessorContext, ProcessorMetadata, ProcessorResult,
13};
14use crate::implementations::nikon;
15use crate::types::{Result, TagValue};
16use std::collections::HashMap;
17use tracing::debug;
18
19/// Nikon Encrypted Data processor using existing implementation
20///
21/// Processes Nikon encrypted maker note data using the existing `nikon::encryption`
22/// implementation. This processor detects encryption and manages keys for decryption.
23///
24/// ## ExifTool Reference
25///
26/// Nikon.pm ProcessNikonEncrypted function and encryption key management
27pub struct NikonEncryptedDataProcessor;
28
29impl BinaryDataProcessor for NikonEncryptedDataProcessor {
30    fn can_process(&self, context: &ProcessorContext) -> ProcessorCapability {
31        // Check for Nikon manufacturer (exact match with ExifTool patterns)
32        if !context.is_manufacturer("NIKON CORPORATION") && !context.is_manufacturer("NIKON") {
33            return ProcessorCapability::Incompatible;
34        }
35
36        // Perfect match for Nikon encrypted data tables
37        if context.table_name.starts_with("Nikon::")
38            && (context.table_name.contains("Encrypted") || context.table_name.contains("LensData"))
39        {
40            return ProcessorCapability::Perfect;
41        }
42
43        // Good match for Nikon-specific tables if we have encryption keys available
44        if context.table_name.starts_with("Nikon::")
45            && context.get_nikon_encryption_keys().is_some()
46        {
47            return ProcessorCapability::Good; // Can process with keys
48        }
49
50        // Only compatible with Nikon-specific tables
51        ProcessorCapability::Incompatible
52    }
53
54    fn process_data(&self, data: &[u8], context: &ProcessorContext) -> Result<ProcessorResult> {
55        debug!(
56            "Processing Nikon encrypted data with {} bytes for table: {}",
57            data.len(),
58            context.table_name
59        );
60
61        let mut result = ProcessorResult::new();
62
63        // Get encryption keys from context if available
64        let keys = context.get_nikon_encryption_keys();
65
66        // Detect encryption signature and process accordingly
67        if data.len() >= 4 {
68            // Check for common Nikon encryption signatures
69            // ExifTool: Nikon.pm encryption detection patterns
70            let has_encryption_signature = detect_nikon_encryption_signature(data);
71
72            if has_encryption_signature {
73                result.add_tag(
74                    "EncryptionDetected".to_string(),
75                    "Nikon encryption detected".into(),
76                );
77
78                // Process encryption status based on available keys
79                if let Some(encryption_keys) = keys {
80                    let (ref serial, count) = encryption_keys;
81                    if !serial.is_empty() && count > 0 {
82                        // We have keys - could decrypt if implemented
83                        let status = format!(
84                            "Encrypted data with keys available (serial: {serial}, count: {count})"
85                        );
86                        result.add_tag("EncryptionStatus".to_string(), TagValue::String(status));
87
88                        // Use existing encryption processing (Phase 1: detection only)
89                        // This delegates to the proven Nikon encryption logic
90                        // For Phase 1, we work with the tuple format from get_nikon_encryption_keys
91                        let mut nikon_keys = nikon::encryption::NikonEncryptionKeys::new(
92                            context.manufacturer.clone().unwrap_or_default(),
93                        );
94                        nikon_keys.store_serial_key(serial.clone());
95                        nikon_keys.store_count_key(count);
96                        process_encrypted_data_with_existing_impl(
97                            data,
98                            context,
99                            &nikon_keys,
100                            &mut result,
101                        )?;
102                    } else {
103                        result.add_tag(
104                            "EncryptionStatus".to_string(),
105                            TagValue::String(
106                                "Encrypted data detected - encryption keys incomplete".to_string(),
107                            ),
108                        );
109                        result.add_warning(
110                            "Nikon encryption keys not available for decryption".to_string(),
111                        );
112                    }
113                } else {
114                    result.add_tag(
115                        "EncryptionStatus".to_string(),
116                        TagValue::String(
117                            "Encrypted data detected - no encryption context".to_string(),
118                        ),
119                    );
120                    result.add_warning("No Nikon encryption context available".to_string());
121                }
122            } else {
123                // No encryption detected - process as regular binary data
124                result.add_tag(
125                    "EncryptionStatus".to_string(),
126                    "No encryption detected".into(),
127                );
128
129                // Could process as regular Nikon binary data here
130                process_unencrypted_nikon_data(data, context, &mut result)?;
131            }
132        } else {
133            result.add_warning("Nikon data too short for encryption detection".to_string());
134        }
135
136        debug!(
137            "Nikon encrypted data processor extracted {} tags with {} warnings",
138            result.extracted_tags.len(),
139            result.warnings.len()
140        );
141
142        Ok(result)
143    }
144
145    fn get_metadata(&self) -> ProcessorMetadata {
146        ProcessorMetadata::new(
147            "Nikon Encrypted Data Processor".to_string(),
148            "Detects and processes Nikon encrypted maker note data".to_string(),
149        )
150        .with_manufacturer("NIKON CORPORATION".to_string())
151        .with_manufacturer("NIKON".to_string())
152        .with_required_context("manufacturer".to_string())
153        .with_optional_context("nikon_encryption_keys".to_string())
154        .with_example_condition("manufacturer.contains('NIKON') && encryption_detected".to_string())
155    }
156}
157
158/// Nikon AF Info processor for autofocus data
159///
160/// Processes Nikon AF (autofocus) information using existing implementation logic.
161/// This handles both encrypted and unencrypted AF data depending on the camera model.
162///
163/// ## ExifTool Reference
164///
165/// Nikon.pm AF processing and AFInfo table handling
166pub struct NikonAFInfoProcessor;
167
168impl BinaryDataProcessor for NikonAFInfoProcessor {
169    fn can_process(&self, context: &ProcessorContext) -> ProcessorCapability {
170        // Only process Nikon-specific tables, not standard EXIF directories
171        // ExifTool Nikon.pm only processes Nikon:: prefixed tables
172        if !context.is_manufacturer("NIKON CORPORATION") && !context.is_manufacturer("NIKON") {
173            return ProcessorCapability::Incompatible;
174        }
175
176        // Perfect match for Nikon AF-related tables
177        if context.table_name.starts_with("Nikon::")
178            && (context.table_name.contains("AFInfo") || context.table_name.contains("AF"))
179            && !context.table_name.contains("Encrypted")
180        {
181            return ProcessorCapability::Perfect;
182        }
183
184        // Good match for other Nikon-specific tables that might contain AF info
185        if context.table_name.starts_with("Nikon::") && !context.table_name.contains("Encrypted") {
186            return ProcessorCapability::Good;
187        }
188
189        // Incompatible with non-Nikon tables (like ExifIFD, GPS, etc.)
190        ProcessorCapability::Incompatible
191    }
192
193    fn process_data(&self, data: &[u8], context: &ProcessorContext) -> Result<ProcessorResult> {
194        debug!(
195            "Processing Nikon AF data with {} bytes for table: {}",
196            data.len(),
197            context.table_name
198        );
199
200        let mut result = ProcessorResult::new();
201
202        // Delegate to existing Nikon AF processing implementation
203        // For Phase 1, create basic AF data extraction
204        // TODO: Phase 2+ will implement full AF processing logic
205        let mut af_data = HashMap::new();
206        if data.len() >= 2 {
207            let value = context
208                .byte_order
209                .unwrap_or(crate::tiff_types::ByteOrder::LittleEndian)
210                .read_u16(data, 0)?;
211            af_data.insert("AFValue".to_string(), crate::types::TagValue::U16(value));
212        }
213
214        for (tag_name, tag_value) in af_data {
215            result.add_tag(tag_name, tag_value);
216        }
217
218        // Add metadata about the AF processing
219        if let Some(model) = &context.model {
220            result.add_tag(
221                "ProcessedModel".to_string(),
222                TagValue::String(model.clone()),
223            );
224        }
225
226        if result.extracted_tags.is_empty() {
227            result.add_warning("No Nikon AF data extracted".to_string());
228        } else {
229            debug!(
230                "Nikon AF processor extracted {} tags",
231                result.extracted_tags.len()
232            );
233        }
234
235        Ok(result)
236    }
237
238    fn get_metadata(&self) -> ProcessorMetadata {
239        ProcessorMetadata::new(
240            "Nikon AF Info Processor".to_string(),
241            "Processes Nikon autofocus information and AF point data".to_string(),
242        )
243        .with_manufacturer("NIKON CORPORATION".to_string())
244        .with_manufacturer("NIKON".to_string())
245        .with_required_context("manufacturer".to_string())
246        .with_optional_context("model".to_string())
247        .with_example_condition(
248            "manufacturer.contains('NIKON') && table.contains('AF')".to_string(),
249        )
250    }
251}
252
253/// Nikon Lens Data processor for lens information
254///
255/// Processes Nikon lens data which may be encrypted depending on the camera model.
256/// Delegates to existing lens database and processing logic.
257///
258/// ## ExifTool Reference
259///
260/// Nikon.pm LensData processing and lens database lookup
261pub struct NikonLensDataProcessor;
262
263impl BinaryDataProcessor for NikonLensDataProcessor {
264    fn can_process(&self, context: &ProcessorContext) -> ProcessorCapability {
265        // Check for Nikon manufacturer
266        if !context.is_manufacturer("NIKON CORPORATION") && !context.is_manufacturer("NIKON") {
267            return ProcessorCapability::Incompatible;
268        }
269
270        // Perfect match for Nikon-specific lens data tables
271        if context.table_name.starts_with("Nikon::")
272            && (context.table_name.contains("LensData") || context.table_name.contains("Lens"))
273        {
274            return ProcessorCapability::Perfect;
275        }
276
277        ProcessorCapability::Incompatible
278    }
279
280    fn process_data(&self, data: &[u8], context: &ProcessorContext) -> Result<ProcessorResult> {
281        debug!(
282            "Processing Nikon lens data with {} bytes for table: {}",
283            data.len(),
284            context.table_name
285        );
286
287        let mut result = ProcessorResult::new();
288
289        // Check if this lens data is encrypted
290        if detect_nikon_encryption_signature(data) {
291            result.add_warning(
292                "Nikon lens data is encrypted - decryption not implemented".to_string(),
293            );
294            result.add_tag(
295                "LensDataStatus".to_string(),
296                "Encrypted lens data detected".into(),
297            );
298        } else {
299            // Process unencrypted lens data using existing implementation
300            // For Phase 1, create basic lens data extraction
301            // TODO: Phase 2+ will implement full lens database lookup
302            let mut lens_info = HashMap::new();
303            if data.len() >= 4 {
304                let lens_id = context
305                    .byte_order
306                    .unwrap_or(crate::tiff_types::ByteOrder::LittleEndian)
307                    .read_u32(data, 0)?;
308                lens_info.insert("LensID".to_string(), crate::types::TagValue::U32(lens_id));
309            }
310
311            for (tag_name, tag_value) in lens_info {
312                result.add_tag(tag_name, tag_value);
313            }
314
315            result.add_tag(
316                "LensDataStatus".to_string(),
317                "Unencrypted lens data processed".into(),
318            );
319        }
320
321        debug!(
322            "Nikon lens data processor extracted {} tags",
323            result.extracted_tags.len()
324        );
325
326        Ok(result)
327    }
328
329    fn get_metadata(&self) -> ProcessorMetadata {
330        ProcessorMetadata::new(
331            "Nikon Lens Data Processor".to_string(),
332            "Processes Nikon lens information and database lookup".to_string(),
333        )
334        .with_manufacturer("NIKON CORPORATION".to_string())
335        .with_manufacturer("NIKON".to_string())
336        .with_required_context("manufacturer".to_string())
337        .with_example_condition(
338            "manufacturer.contains('NIKON') && table.contains('Lens')".to_string(),
339        )
340    }
341}
342
343/// Detect Nikon encryption signature in data
344///
345/// This function checks for common Nikon encryption patterns to determine
346/// if the data is encrypted and needs special processing.
347///
348/// ## ExifTool Reference
349///
350/// Nikon.pm encryption detection patterns
351fn detect_nikon_encryption_signature(data: &[u8]) -> bool {
352    if data.len() < 4 {
353        return false;
354    }
355
356    // Check for common Nikon encryption signatures
357    // ExifTool: Nikon.pm ProcessNikonEncrypted signature detection
358    match &data[0..4] {
359        [0x02, 0x00, 0x00, 0x00] => true, // Type 2 encryption
360        [0x02, 0x04, _, _] => true,       // Type 2.04 encryption
361        [0x04, 0x02, _, _] => true,       // Type 4.02 encryption
362        _ => false,
363    }
364}
365
366/// Process encrypted data using existing implementation
367///
368/// This function delegates to the existing Nikon encryption processing
369/// logic while integrating with the new processor system.
370fn process_encrypted_data_with_existing_impl(
371    _data: &[u8],
372    context: &ProcessorContext,
373    keys: &nikon::encryption::NikonEncryptionKeys,
374    result: &mut ProcessorResult,
375) -> Result<()> {
376    // This is Phase 1 implementation - detection and key management only
377    // Real decryption would be implemented in later phases
378
379    debug!("Processing encrypted Nikon data with existing implementation");
380
381    // Validate encryption keys
382    let manufacturer = context.manufacturer.as_deref().unwrap_or("Unknown");
383    if let Err(e) = nikon::encryption::validate_encryption_keys(keys, manufacturer) {
384        result.add_warning(format!("Encryption key validation failed: {e}"));
385        return Ok(());
386    }
387
388    // Add information about the encryption context
389    result.add_tag(
390        "EncryptionKeys".to_string(),
391        TagValue::string(format!(
392            "Serial: {}, Count: {}",
393            keys.get_serial_key().unwrap_or("none"),
394            keys.get_count_key()
395                .map(|c| c.to_string())
396                .unwrap_or("none".to_string())
397        )),
398    );
399
400    // TODO: Phase 2+ implementation would add actual decryption here
401    // For now, we just detect and report the encryption status
402
403    Ok(())
404}
405
406/// Process unencrypted Nikon data
407///
408/// This function processes Nikon data that is not encrypted using
409/// standard binary data processing techniques.
410fn process_unencrypted_nikon_data(
411    data: &[u8],
412    context: &ProcessorContext,
413    result: &mut ProcessorResult,
414) -> Result<()> {
415    debug!("Processing unencrypted Nikon data");
416
417    // Basic processing for unencrypted Nikon data
418    // This would be expanded based on the specific table type
419
420    let byte_order = context
421        .byte_order
422        .unwrap_or(crate::tiff_types::ByteOrder::LittleEndian);
423
424    if data.len() >= 2 {
425        // Extract some basic information
426        let value = byte_order.read_u16(data, 0)?;
427        result.add_tag("FirstValue".to_string(), TagValue::U16(value));
428    }
429
430    if data.len() >= 4 {
431        let value = byte_order.read_u32(data, 0)?;
432        result.add_tag("FirstU32Value".to_string(), TagValue::U32(value));
433    }
434
435    result.add_tag("DataLength".to_string(), TagValue::U32(data.len() as u32));
436
437    Ok(())
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443    use crate::formats::FileFormat;
444    use crate::processor_registry::ProcessorContext;
445
446    #[test]
447    fn test_nikon_encrypted_processor_capability() {
448        let processor = NikonEncryptedDataProcessor;
449
450        // Perfect match for Nikon encrypted data
451        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::Encrypted".to_string())
452            .with_manufacturer("NIKON CORPORATION".to_string());
453
454        assert_eq!(
455            processor.can_process(&context),
456            ProcessorCapability::Perfect
457        );
458
459        // Incompatible for non-Nikon
460        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::Encrypted".to_string())
461            .with_manufacturer("Canon".to_string());
462
463        assert_eq!(
464            processor.can_process(&context),
465            ProcessorCapability::Incompatible
466        );
467    }
468
469    #[test]
470    fn test_nikon_af_processor_capability() {
471        let processor = NikonAFInfoProcessor;
472
473        // Perfect match for Nikon AF data
474        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::AFInfo".to_string())
475            .with_manufacturer("NIKON CORPORATION".to_string());
476
477        assert_eq!(
478            processor.can_process(&context),
479            ProcessorCapability::Perfect
480        );
481
482        // Good match for general Nikon data
483        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::Main".to_string())
484            .with_manufacturer("NIKON".to_string());
485
486        assert_eq!(processor.can_process(&context), ProcessorCapability::Good);
487    }
488
489    #[test]
490    fn test_nikon_lens_processor_capability() {
491        let processor = NikonLensDataProcessor;
492
493        // Perfect match for Nikon lens data
494        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::LensData".to_string())
495            .with_manufacturer("NIKON CORPORATION".to_string());
496
497        assert_eq!(
498            processor.can_process(&context),
499            ProcessorCapability::Perfect
500        );
501
502        // Incompatible for non-lens data
503        let context = ProcessorContext::new(FileFormat::Jpeg, "Nikon::Other".to_string())
504            .with_manufacturer("NIKON CORPORATION".to_string());
505
506        assert_eq!(
507            processor.can_process(&context),
508            ProcessorCapability::Incompatible
509        );
510    }
511
512    #[test]
513    fn test_encryption_signature_detection() {
514        // Test Type 2 encryption signature
515        let encrypted_data = vec![0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04];
516        assert!(detect_nikon_encryption_signature(&encrypted_data));
517
518        // Test non-encrypted data
519        let normal_data = vec![0x01, 0x02, 0x03, 0x04];
520        assert!(!detect_nikon_encryption_signature(&normal_data));
521
522        // Test short data
523        let short_data = vec![0x01, 0x02];
524        assert!(!detect_nikon_encryption_signature(&short_data));
525    }
526
527    #[test]
528    fn test_processor_metadata() {
529        let processor = NikonEncryptedDataProcessor;
530        let metadata = processor.get_metadata();
531
532        assert_eq!(metadata.name, "Nikon Encrypted Data Processor");
533        assert!(metadata
534            .supported_manufacturers
535            .contains(&"NIKON CORPORATION".to_string()));
536        assert!(metadata
537            .supported_manufacturers
538            .contains(&"NIKON".to_string()));
539        assert!(metadata
540            .required_context
541            .contains(&"manufacturer".to_string()));
542    }
543}