Skip to main content

paysec_keyblock/tr31_2018/
key_block_header.rs

1//! Module for TR-31 Key Block Headers.
2//!
3//! A TR-31 key block header contains the attributes associated with a
4//! protected key, including its key block version, usage, algorithm, mode of
5//! use, exportability, and optional blocks.
6//!
7//! The fixed portion of the header is 16 ASCII characters. Optional blocks,
8//! when present, immediately follow the fixed header.
9
10use super::error::KeyBlockHeaderError;
11use super::header_constants::{
12    ALLOWED_ALGORITHMS, ALLOWED_EXPORTABILITIES, ALLOWED_KEY_USAGES, ALLOWED_MODES_OF_USE,
13    ALLOWED_VERSION_IDS,
14};
15use super::opt_block::OptBlock;
16
17/// Represents a TR-31 key block header.
18#[derive(Debug, PartialEq)]
19pub struct KeyBlockHeader {
20    version_id: String,
21    kb_length: u16,
22    key_usage: String,
23    algorithm: String,
24    mode_of_use: String,
25    key_version_number: String,
26    exportability: String,
27    num_opt_blocks: u8,
28    reserved_field: String,
29    opt_blocks: Option<Box<OptBlock>>,
30}
31
32impl KeyBlockHeader {
33    /// Create a new empty key block header.
34    pub fn new_empty() -> Self {
35        Self {
36            version_id: String::new(),
37            kb_length: 0,
38            key_usage: String::new(),
39            algorithm: String::new(),
40            mode_of_use: String::new(),
41            key_version_number: String::new(),
42            exportability: String::new(),
43            num_opt_blocks: 0,
44            reserved_field: "00".to_string(),
45            opt_blocks: None,
46        }
47    }
48
49    /// Create a new key block header from individual header values.
50    pub fn new_with_values(
51        version_id: &str,
52        key_usage: &str,
53        algorithm: &str,
54        mode_of_use: &str,
55        key_version_number: &str,
56        exportability: &str,
57    ) -> Result<Self, KeyBlockHeaderError> {
58        let mut header = Self::new_empty();
59
60        header.set_version_id(version_id)?;
61
62        header.set_key_usage(key_usage)?;
63
64        header.set_algorithm(algorithm)?;
65
66        header.set_mode_of_use(mode_of_use)?;
67
68        header.set_key_version_number(key_version_number)?;
69
70        header.set_exportability(exportability)?;
71
72        Ok(header)
73    }
74
75    /// Parse a key block header from its string representation.
76    ///
77    /// The input may contain additional key block data after the header.
78    /// Optional blocks are parsed according to the number declared in the
79    /// fixed header.
80    pub fn new_from_str(header_str: &str) -> Result<Self, KeyBlockHeaderError> {
81        const FIXED_HEADER_LENGTH: usize = 16;
82
83        if !header_str.is_ascii() {
84            return Err(KeyBlockHeaderError::NonAsciiHeader);
85        }
86
87        if header_str.len() < FIXED_HEADER_LENGTH {
88            return Err(KeyBlockHeaderError::InvalidDataLength {
89                minimum: FIXED_HEADER_LENGTH,
90                actual: header_str.len(),
91            });
92        }
93
94        let version_id = header_str[0..1].to_string();
95
96        let kb_length = header_str[1..5]
97            .parse::<u16>()
98            .map_err(|_| KeyBlockHeaderError::InvalidKeyBlockLength)?;
99
100        let key_usage = header_str[5..7].to_string();
101
102        let algorithm = header_str[7..8].to_string();
103
104        let mode_of_use = header_str[8..9].to_string();
105
106        let key_version_number = header_str[9..11].to_string();
107
108        let exportability = header_str[11..12].to_string();
109
110        let num_optional_blocks = header_str[12..14]
111            .parse::<u8>()
112            .map_err(|_| KeyBlockHeaderError::InvalidNumberOfOptionalBlocks)?;
113
114        let reserved_field = header_str[14..16].to_string();
115
116        let mut header = Self::new_empty();
117
118        header.set_version_id(&version_id)?;
119
120        header.set_kb_length(kb_length)?;
121
122        header.set_key_usage(&key_usage)?;
123
124        header.set_algorithm(&algorithm)?;
125
126        header.set_mode_of_use(&mode_of_use)?;
127
128        header.set_key_version_number(&key_version_number)?;
129
130        header.set_exportability(&exportability)?;
131
132        header.set_num_optional_blocks(num_optional_blocks)?;
133
134        header.set_reserved_field(&reserved_field)?;
135
136        if num_optional_blocks > 0 && header_str.len() < 20 {
137            return Err(KeyBlockHeaderError::InvalidHeaderLengthWithOptionalBlocks {
138                minimum: 20,
139                actual: header_str.len(),
140            });
141        }
142
143        if num_optional_blocks > 0 {
144            let opt_block_str = &header_str[16..];
145
146            let opt_block = OptBlock::new_from_str(opt_block_str, num_optional_blocks as usize)
147                .map_err(KeyBlockHeaderError::FailedToParseOptionalBlocks)?;
148
149            header.opt_blocks = Some(Box::new(opt_block));
150        }
151
152        Ok(header)
153    }
154
155    /// Export the key block header to its ASCII representation.
156    pub fn export_str(&self) -> Result<String, KeyBlockHeaderError> {
157        if self.version_id.is_empty()
158            || self.key_usage.is_empty()
159            || self.algorithm.is_empty()
160            || self.mode_of_use.is_empty()
161            || self.key_version_number.is_empty()
162            || self.exportability.is_empty()
163            || self.reserved_field.is_empty()
164        {
165            return Err(KeyBlockHeaderError::ExportFailedEmptyFields);
166        }
167
168        let mut header_str = String::new();
169
170        header_str.push_str(self.version_id());
171
172        header_str.push_str(&format!("{:04}", self.kb_length(),));
173
174        header_str.push_str(self.key_usage());
175
176        header_str.push_str(self.algorithm());
177
178        header_str.push_str(self.mode_of_use());
179
180        header_str.push_str(self.key_version_number());
181
182        header_str.push_str(self.exportability());
183
184        header_str.push_str(&format!("{:02}", self.num_opt_blocks,));
185
186        header_str.push_str(self.reserved_field());
187
188        if let Some(opt_blocks) = &self.opt_blocks {
189            header_str.push_str(&opt_blocks.export_str()?);
190        }
191
192        Ok(header_str)
193    }
194
195    /// Set the key block version identifier.
196    pub fn set_version_id(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
197        if ALLOWED_VERSION_IDS.contains(&value) {
198            self.version_id = value.to_string();
199
200            Ok(())
201        } else {
202            Err(KeyBlockHeaderError::InvalidVersionId(value.to_string()))
203        }
204    }
205
206    /// Return the key block version identifier.
207    pub fn version_id(&self) -> &str {
208        &self.version_id
209    }
210
211    /// Set the complete key block length.
212    pub fn set_kb_length(&mut self, value: u16) -> Result<(), KeyBlockHeaderError> {
213        if value > 9999 {
214            return Err(KeyBlockHeaderError::InvalidKeyBlockLength);
215        }
216
217        self.kb_length = value;
218
219        Ok(())
220    }
221
222    /// Return the complete key block length.
223    pub fn kb_length(&self) -> u16 {
224        self.kb_length
225    }
226
227    /// Set the key usage.
228    pub fn set_key_usage(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
229        if ALLOWED_KEY_USAGES.contains(&value) {
230            self.key_usage = value.to_string();
231
232            Ok(())
233        } else {
234            Err(KeyBlockHeaderError::InvalidKeyUsage(value.to_string()))
235        }
236    }
237
238    /// Return the key usage.
239    pub fn key_usage(&self) -> &str {
240        &self.key_usage
241    }
242
243    /// Set the protected-key algorithm.
244    pub fn set_algorithm(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
245        if ALLOWED_ALGORITHMS.contains(&value) {
246            self.algorithm = value.to_string();
247
248            Ok(())
249        } else {
250            Err(KeyBlockHeaderError::InvalidAlgorithm(value.to_string()))
251        }
252    }
253
254    /// Return the protected-key algorithm.
255    pub fn algorithm(&self) -> &str {
256        &self.algorithm
257    }
258
259    /// Set the key mode of use.
260    pub fn set_mode_of_use(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
261        if ALLOWED_MODES_OF_USE.contains(&value) {
262            self.mode_of_use = value.to_string();
263
264            Ok(())
265        } else {
266            Err(KeyBlockHeaderError::InvalidModeOfUse(value.to_string()))
267        }
268    }
269
270    /// Return the key mode of use.
271    pub fn mode_of_use(&self) -> &str {
272        &self.mode_of_use
273    }
274
275    /// Set the key version number.
276    pub fn set_key_version_number(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
277        if value.len() != 2 {
278            return Err(KeyBlockHeaderError::InvalidKeyVersionNumberLength(
279                value.to_string(),
280            ));
281        }
282
283        if !value.is_ascii() {
284            return Err(KeyBlockHeaderError::InvalidKeyVersionNumberEncoding(
285                value.to_string(),
286            ));
287        }
288
289        self.key_version_number = value.to_string();
290
291        Ok(())
292    }
293
294    /// Return the key version number.
295    pub fn key_version_number(&self) -> &str {
296        &self.key_version_number
297    }
298
299    /// Set the exportability attribute.
300    pub fn set_exportability(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
301        if ALLOWED_EXPORTABILITIES.contains(&value) {
302            self.exportability = value.to_string();
303
304            Ok(())
305        } else {
306            Err(KeyBlockHeaderError::InvalidExportability(value.to_string()))
307        }
308    }
309
310    /// Return the exportability attribute.
311    pub fn exportability(&self) -> &str {
312        &self.exportability
313    }
314
315    /// Set the number of optional blocks declared in the header.
316    pub fn set_num_optional_blocks(&mut self, value: u8) -> Result<(), KeyBlockHeaderError> {
317        const MAX_OPTIONAL_BLOCKS: u8 = 99;
318
319        if value > MAX_OPTIONAL_BLOCKS {
320            return Err(KeyBlockHeaderError::TooManyOptionalBlocks {
321                maximum: MAX_OPTIONAL_BLOCKS,
322                actual: value,
323            });
324        }
325
326        self.num_opt_blocks = value;
327
328        Ok(())
329    }
330
331    /// Return the number of optional blocks declared in the header.
332    pub fn num_optional_blocks(&self) -> u8 {
333        self.num_opt_blocks
334    }
335
336    /// Set the TR-31 reserved header field.
337    pub fn set_reserved_field(&mut self, value: &str) -> Result<(), KeyBlockHeaderError> {
338        if value == "00" {
339            self.reserved_field = value.to_string();
340
341            Ok(())
342        } else {
343            Err(KeyBlockHeaderError::InvalidReservedField(value.to_string()))
344        }
345    }
346
347    /// Return the reserved header field.
348    pub fn reserved_field(&self) -> &str {
349        &self.reserved_field
350    }
351
352    /// Replace the linked optional blocks and update their count.
353    pub fn set_opt_blocks(&mut self, opt_blocks: Option<Box<OptBlock>>) {
354        self.opt_blocks = opt_blocks;
355
356        self.num_opt_blocks = 0;
357
358        if let Some(opt_block) = &self.opt_blocks {
359            let mut current_block = opt_block.as_ref();
360
361            self.num_opt_blocks = 1;
362
363            while let Some(next_block) = current_block.next() {
364                self.num_opt_blocks += 1;
365                current_block = next_block;
366            }
367        }
368    }
369
370    /// Append one or more optional blocks to the existing optional-block
371    /// chain.
372    pub fn append_opt_blocks(&mut self, opt_block_to_append: OptBlock) {
373        let mut additional_blocks_count = 1;
374
375        let mut current_block = &opt_block_to_append;
376
377        while let Some(next_block) = current_block.next() {
378            additional_blocks_count += 1;
379
380            current_block = next_block;
381        }
382
383        match &mut self.opt_blocks {
384            Some(existing_opt_block) => {
385                existing_opt_block.append(opt_block_to_append);
386            }
387
388            None => {
389                self.opt_blocks = Some(Box::new(opt_block_to_append));
390            }
391        }
392
393        self.num_opt_blocks += additional_blocks_count;
394    }
395
396    /// Return the optional-block chain.
397    pub fn opt_blocks(&self) -> &Option<Box<OptBlock>> {
398        &self.opt_blocks
399    }
400
401    /// Return the complete encoded header length, including optional blocks.
402    pub fn len(&self) -> usize {
403        let mut header_length = 16;
404
405        if let Some(opt_blocks) = &self.opt_blocks {
406            header_length += opt_blocks.total_length();
407        }
408
409        header_length
410    }
411
412    /// Finalize the header by padding optional blocks to the cipher block
413    /// boundary when required.
414    pub fn finalize(&mut self) -> Result<(), KeyBlockHeaderError> {
415        let block_size = if self.version_id == "D" { 16 } else { 8 };
416
417        let header_length = self.len();
418
419        if let Some(opt_blocks) = &mut self.opt_blocks {
420            if header_length % block_size != 0 {
421                let mut padding_needed = block_size - (header_length % block_size);
422
423                // A padding optional block must contain at least:
424                //
425                // - two-byte ID,
426                // - two-byte length,
427                // - two padding characters.
428                if padding_needed < 6 {
429                    padding_needed += block_size;
430                }
431
432                let padding_data_length = padding_needed - 4;
433
434                let padding_data = "0".repeat(padding_data_length);
435
436                let padding_block = OptBlock::new("PB", &padding_data, None)?;
437
438                opt_blocks.append(padding_block);
439
440                self.num_opt_blocks += 1;
441            }
442        }
443
444        Ok(())
445    }
446}
447
448#[test]
449fn test_header_invalid_version_typed_error() {
450    let result = KeyBlockHeader::new_with_values("X", "P0", "A", "E", "00", "E");
451
452    assert!(matches!(
453        result,
454        Err(KeyBlockHeaderError::InvalidVersionId(_))
455    ));
456}
457
458#[test]
459fn test_header_optional_block_error_is_preserved() {
460    use crate::OptBlockError;
461
462    let result = KeyBlockHeader::new_from_str("D0020P0AE00E0100XX04");
463
464    assert!(matches!(
465        result,
466        Err(KeyBlockHeaderError::FailedToParseOptionalBlocks(
467            OptBlockError::InvalidId(_)
468        ))
469    ));
470}