150 lines
4 KiB
Rust
150 lines
4 KiB
Rust
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
//! Fingerprint for a single key slot
|
|
|
|
use anyhow::anyhow;
|
|
use nom::{bytes::complete as bytes, combinator, sequence};
|
|
use std::convert::TryFrom;
|
|
use std::convert::TryInto;
|
|
use std::fmt;
|
|
|
|
use crate::card_do::{Fingerprint, KeySet};
|
|
use crate::Error;
|
|
|
|
impl From<[u8; 20]> for Fingerprint {
|
|
fn from(data: [u8; 20]) -> Self {
|
|
Self(data)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for Fingerprint {
|
|
type Error = Error;
|
|
|
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
|
log::trace!(
|
|
"Fingerprint from input: {:x?}, len {}",
|
|
input,
|
|
input.len()
|
|
);
|
|
|
|
// FIXME: return error
|
|
assert_eq!(input.len(), 20);
|
|
|
|
let array: [u8; 20] = input.try_into().unwrap();
|
|
Ok(array.into())
|
|
}
|
|
}
|
|
|
|
impl Fingerprint {
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Fingerprint {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{:X}", self)
|
|
}
|
|
}
|
|
|
|
impl fmt::UpperHex for Fingerprint {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
for b in &self.0 {
|
|
write!(f, "{:02X}", b)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Fingerprint {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_tuple("Fingerprint")
|
|
.field(&self.to_string())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
fn fingerprint(input: &[u8]) -> nom::IResult<&[u8], Option<Fingerprint>> {
|
|
combinator::map(bytes::take(20u8), |i: &[u8]| {
|
|
if i.iter().any(|&c| c > 0) {
|
|
// We requested 20 bytes, so we can unwrap here
|
|
let i: [u8; 20] = i.try_into().unwrap();
|
|
Some(i.into())
|
|
} else {
|
|
None
|
|
}
|
|
})(input)
|
|
}
|
|
|
|
fn fingerprints(input: &[u8]) -> nom::IResult<&[u8], KeySet<Fingerprint>> {
|
|
combinator::into(sequence::tuple((fingerprint, fingerprint, fingerprint)))(
|
|
input,
|
|
)
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for KeySet<Fingerprint> {
|
|
type Error = Error;
|
|
|
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
|
log::trace!(
|
|
"Fingerprint from input: {:x?}, len {}",
|
|
input,
|
|
input.len()
|
|
);
|
|
|
|
// The input may be longer than 3 fingerprint, don't fail if it hasn't
|
|
// been completely consumed.
|
|
self::fingerprints(input)
|
|
.map(|res| res.1)
|
|
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
|
|
.map_err(Error::InternalError)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test() {
|
|
let data3 = [
|
|
0xb7, 0xcd, 0x9f, 0x76, 0x37, 0x1e, 0x7, 0x7f, 0x76, 0x1c, 0x82,
|
|
0x65, 0x55, 0x54, 0x3e, 0x6d, 0x65, 0x6d, 0x1d, 0x80, 0x62, 0xd7,
|
|
0x34, 0x22, 0x65, 0xd2, 0xef, 0x33, 0x64, 0xe3, 0x79, 0x52, 0xd9,
|
|
0x5e, 0x94, 0x20, 0x5f, 0x4c, 0xce, 0x8b, 0x3f, 0x9, 0x7a, 0xf2,
|
|
0xfd, 0x76, 0xa5, 0xa7, 0x57, 0x9b, 0x51, 0x1f, 0xf, 0x44, 0x9a,
|
|
0x25, 0x80, 0x2d, 0xb2, 0xb8,
|
|
];
|
|
|
|
let fp_set: KeySet<Fingerprint> = (&data3[..])
|
|
.try_into()
|
|
.expect("failed to parse fingerprint set");
|
|
|
|
assert_eq!(
|
|
format!("{}", fp_set.signature().unwrap()),
|
|
"B7CD9F76371E077F761C826555543E6D656D1D80"
|
|
);
|
|
assert_eq!(
|
|
format!("{}", fp_set.decryption().unwrap()),
|
|
"62D7342265D2EF3364E37952D95E94205F4CCE8B"
|
|
);
|
|
assert_eq!(
|
|
format!("{}", fp_set.authentication().unwrap()),
|
|
"3F097AF2FD76A5A7579B511F0F449A25802DB2B8"
|
|
);
|
|
|
|
let data1 = [
|
|
0xb7, 0xcd, 0x9f, 0x76, 0x37, 0x1e, 0x7, 0x7f, 0x76, 0x1c, 0x82,
|
|
0x65, 0x55, 0x54, 0x3e, 0x6d, 0x65, 0x6d, 0x1d, 0x80,
|
|
];
|
|
|
|
let fp = Fingerprint::try_from(&data1[..])
|
|
.expect("failed to parse fingerprint");
|
|
|
|
assert_eq!(
|
|
format!("{}", fp),
|
|
"B7CD9F76371E077F761C826555543E6D656D1D80"
|
|
);
|
|
}
|
|
}
|