openpgp_card/ocard/data/
fingerprint.rs

1// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Fingerprint for a single key slot
5
6use std::convert::TryFrom;
7use std::convert::TryInto;
8use std::fmt;
9
10use nom::{bytes::complete as bytes, combinator, sequence};
11
12use crate::ocard::data::{Fingerprint, KeySet};
13use crate::Error;
14
15impl From<[u8; 20]> for Fingerprint {
16    fn from(data: [u8; 20]) -> Self {
17        Self(data)
18    }
19}
20
21impl TryFrom<&[u8]> for Fingerprint {
22    type Error = Error;
23
24    fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
25        log::trace!("Fingerprint from input: {:x?}, len {}", input, input.len());
26
27        if input.len() == 20 {
28            #[allow(clippy::expect_used)]
29            let array: [u8; 20] = input.try_into().expect("checked");
30            Ok(array.into())
31        } else {
32            Err(Error::ParseError(format!(
33                "Unexpected fingerprint length {}",
34                input.len()
35            )))
36        }
37    }
38}
39
40impl Fingerprint {
41    pub fn as_bytes(&self) -> &[u8] {
42        &self.0
43    }
44}
45
46impl fmt::Display for Fingerprint {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        write!(f, "{self:X}")
49    }
50}
51
52impl fmt::UpperHex for Fingerprint {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        for b in &self.0 {
55            write!(f, "{b:02X}")?;
56        }
57        Ok(())
58    }
59}
60
61impl fmt::Debug for Fingerprint {
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        f.debug_tuple("Fingerprint")
64            .field(&self.to_string())
65            .finish()
66    }
67}
68
69fn fingerprint(input: &[u8]) -> nom::IResult<&[u8], Option<Fingerprint>> {
70    combinator::map(bytes::take(20u8), |i: &[u8]| {
71        if i.iter().any(|&c| c > 0) {
72            // We requested 20 bytes, so we can expect here
73            #[allow(clippy::expect_used)]
74            let i: [u8; 20] = i.try_into().expect("20 taken");
75            Some(i.into())
76        } else {
77            None
78        }
79    })(input)
80}
81
82fn fingerprints(input: &[u8]) -> nom::IResult<&[u8], KeySet<Fingerprint>> {
83    combinator::into(sequence::tuple((fingerprint, fingerprint, fingerprint)))(input)
84}
85
86impl TryFrom<&[u8]> for KeySet<Fingerprint> {
87    type Error = Error;
88
89    fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
90        log::trace!("Fingerprint from input: {:x?}, len {}", input, input.len());
91
92        // The input may be longer than 3 fingerprint, don't fail if it hasn't
93        // been completely consumed.
94        self::fingerprints(input)
95            .map(|res| res.1)
96            .map_err(|_err| Error::ParseError("Parsing failed".into()))
97    }
98}
99
100#[cfg(test)]
101mod test {
102    use super::*;
103
104    #[test]
105    fn test() {
106        let data3 = [
107            0xb7, 0xcd, 0x9f, 0x76, 0x37, 0x1e, 0x7, 0x7f, 0x76, 0x1c, 0x82, 0x65, 0x55, 0x54,
108            0x3e, 0x6d, 0x65, 0x6d, 0x1d, 0x80, 0x62, 0xd7, 0x34, 0x22, 0x65, 0xd2, 0xef, 0x33,
109            0x64, 0xe3, 0x79, 0x52, 0xd9, 0x5e, 0x94, 0x20, 0x5f, 0x4c, 0xce, 0x8b, 0x3f, 0x9,
110            0x7a, 0xf2, 0xfd, 0x76, 0xa5, 0xa7, 0x57, 0x9b, 0x51, 0x1f, 0xf, 0x44, 0x9a, 0x25,
111            0x80, 0x2d, 0xb2, 0xb8,
112        ];
113
114        let fp_set: KeySet<Fingerprint> = (&data3[..])
115            .try_into()
116            .expect("failed to parse fingerprint set");
117
118        assert_eq!(
119            format!("{}", fp_set.signature().unwrap()),
120            "B7CD9F76371E077F761C826555543E6D656D1D80"
121        );
122        assert_eq!(
123            format!("{}", fp_set.decryption().unwrap()),
124            "62D7342265D2EF3364E37952D95E94205F4CCE8B"
125        );
126        assert_eq!(
127            format!("{}", fp_set.authentication().unwrap()),
128            "3F097AF2FD76A5A7579B511F0F449A25802DB2B8"
129        );
130
131        let data1 = [
132            0xb7, 0xcd, 0x9f, 0x76, 0x37, 0x1e, 0x7, 0x7f, 0x76, 0x1c, 0x82, 0x65, 0x55, 0x54,
133            0x3e, 0x6d, 0x65, 0x6d, 0x1d, 0x80,
134        ];
135
136        let fp = Fingerprint::try_from(&data1[..]).expect("failed to parse fingerprint");
137
138        assert_eq!(format!("{fp}"), "B7CD9F76371E077F761C826555543E6D656D1D80");
139    }
140}