openpgp_card/ocard/data/
cardholder.rs

1// SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer <heiko@schaefer.name>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Cardholder Related Data (see spec pg. 22)
5
6use std::convert::TryFrom;
7
8use crate::ocard::data::{CardholderRelatedData, Lang, Sex};
9use crate::ocard::tags::Tags;
10use crate::ocard::tlv::{value::Value, Tlv};
11
12impl CardholderRelatedData {
13    pub fn name(&self) -> Option<&[u8]> {
14        self.name.as_deref()
15    }
16
17    /// The name field is defined as latin1 encoded,
18    /// with ´<´ and ´<<´ filler characters to separate elements and name-parts.
19    ///
20    /// (The filler/separation characters are not processed by this fn)
21    pub(crate) fn latin1_to_string(s: &[u8]) -> String {
22        s.iter().map(|&c| c as char).collect()
23    }
24
25    pub fn lang(&self) -> Option<&[Lang]> {
26        self.lang.as_deref()
27    }
28
29    pub fn sex(&self) -> Option<Sex> {
30        self.sex
31    }
32}
33
34impl TryFrom<&[u8]> for CardholderRelatedData {
35    type Error = crate::Error;
36
37    fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
38        let value = Value::from(data, true)?;
39        let tlv = Tlv::new(Tags::CardholderRelatedData, value);
40
41        let name: Option<Vec<u8>> = tlv.find(Tags::Name).map(|v| v.serialize().to_vec());
42
43        let lang: Option<Vec<Lang>> = tlv.find(Tags::LanguagePref).map(|v| {
44            v.serialize()
45                .chunks(2)
46                .map(|c| match c.len() {
47                    2 => Lang::from(&[c[0], c[1]]),
48                    1 => Lang::from(&[c[0]]),
49                    _ => unreachable!(),
50                })
51                .collect()
52        });
53
54        let sex = tlv
55            .find(Tags::Sex)
56            .map(|v| v.serialize())
57            .filter(|v| v.len() == 1)
58            .map(|v| Sex::from(v[0]));
59
60        Ok(CardholderRelatedData { name, lang, sex })
61    }
62}
63
64#[cfg(test)]
65mod test {
66    use super::*;
67
68    #[test]
69    fn test() {
70        let data = [
71            0x5b, 0x8, 0x42, 0x61, 0x72, 0x3c, 0x3c, 0x46, 0x6f, 0x6f, 0x5f, 0x2d, 0x4, 0x64, 0x65,
72            0x65, 0x6e, 0x5f, 0x35, 0x1, 0x32,
73        ];
74
75        let ch = CardholderRelatedData::try_from(&data[..]).expect("failed to parse cardholder");
76
77        assert_eq!(
78            ch,
79            CardholderRelatedData {
80                name: Some("Bar<<Foo".as_bytes().to_vec()),
81                lang: Some(vec![['d', 'e'].into(), ['e', 'n'].into()]),
82                sex: Some(Sex::Female)
83            }
84        );
85    }
86}