use alloc::{string::String, vec::Vec};
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::sshfp;
use crate::{
error::ProtoResult,
rr::{RData, RecordData, RecordDataDecodable, RecordType},
serialize::{
binary::{BinDecoder, BinEncodable, BinEncoder, DecodeError, Restrict, RestrictedMath},
txt::ParseError,
},
};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub struct TLSA {
pub cert_usage: CertUsage,
pub selector: Selector,
pub matching: Matching,
pub cert_data: Vec<u8>,
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum CertUsage {
#[cfg_attr(feature = "serde", serde(rename = "PKIX-TA"))]
PkixTa,
#[cfg_attr(feature = "serde", serde(rename = "PKIX-EE"))]
PkixEe,
#[cfg_attr(feature = "serde", serde(rename = "DANE-TA"))]
DaneTa,
#[cfg_attr(feature = "serde", serde(rename = "DANE-EE"))]
DaneEe,
Unassigned(u8),
Private,
}
impl From<u8> for CertUsage {
fn from(usage: u8) -> Self {
match usage {
0 => Self::PkixTa,
1 => Self::PkixEe,
2 => Self::DaneTa,
3 => Self::DaneEe,
4..=254 => Self::Unassigned(usage),
255 => Self::Private,
}
}
}
impl From<CertUsage> for u8 {
fn from(usage: CertUsage) -> Self {
match usage {
CertUsage::PkixTa => 0,
CertUsage::PkixEe => 1,
CertUsage::DaneTa => 2,
CertUsage::DaneEe => 3,
CertUsage::Unassigned(usage) => usage,
CertUsage::Private => 255,
}
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Selector {
#[cfg_attr(feature = "serde", serde(rename = "Cert"))]
Full,
#[cfg_attr(feature = "serde", serde(rename = "SPKI"))]
Spki,
Unassigned(u8),
#[cfg_attr(feature = "serde", serde(rename = "PrivSel"))]
Private,
}
impl From<u8> for Selector {
fn from(selector: u8) -> Self {
match selector {
0 => Self::Full,
1 => Self::Spki,
2..=254 => Self::Unassigned(selector),
255 => Self::Private,
}
}
}
impl From<Selector> for u8 {
fn from(selector: Selector) -> Self {
match selector {
Selector::Full => 0,
Selector::Spki => 1,
Selector::Unassigned(selector) => selector,
Selector::Private => 255,
}
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Matching {
#[cfg_attr(feature = "serde", serde(rename = "Full"))]
Raw,
#[cfg_attr(feature = "serde", serde(rename = "SHA2-256"))]
Sha256,
#[cfg_attr(feature = "serde", serde(rename = "SHA2-512"))]
Sha512,
Unassigned(u8),
#[cfg_attr(feature = "serde", serde(rename = "PrivMatch"))]
Private,
}
impl From<u8> for Matching {
fn from(matching: u8) -> Self {
match matching {
0 => Self::Raw,
1 => Self::Sha256,
2 => Self::Sha512,
3..=254 => Self::Unassigned(matching),
255 => Self::Private,
}
}
}
impl From<Matching> for u8 {
fn from(matching: Matching) -> Self {
match matching {
Matching::Raw => 0,
Matching::Sha256 => 1,
Matching::Sha512 => 2,
Matching::Unassigned(matching) => matching,
Matching::Private => 255,
}
}
}
impl TLSA {
pub fn new(
cert_usage: CertUsage,
selector: Selector,
matching: Matching,
cert_data: Vec<u8>,
) -> Self {
Self {
cert_usage,
selector,
matching,
cert_data,
}
}
pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
tokens: I,
) -> Result<Self, ParseError> {
let mut iter = tokens;
let token: &str = iter
.next()
.ok_or(ParseError::Message("TLSA usage field missing"))?;
let cert_usage = CertUsage::from(to_u8(token)?);
let token = iter
.next()
.ok_or(ParseError::Message("TLSA selector field missing"))?;
let selector = to_u8(token)?.into();
let token = iter
.next()
.ok_or(ParseError::Message("TLSA matching field missing"))?;
let matching = to_u8(token)?.into();
let cert_data = iter.fold(String::new(), |mut cert_data, data| {
cert_data.push_str(data);
cert_data
});
let cert_data = sshfp::HEX.decode(cert_data.as_bytes())?;
if !cert_data.is_empty() {
Ok(Self {
cert_usage,
selector,
matching,
cert_data,
})
} else {
Err(ParseError::Message("TLSA data field missing"))
}
}
}
impl BinEncodable for TLSA {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u8(self.cert_usage.into())?;
encoder.emit_u8(self.selector.into())?;
encoder.emit_u8(self.matching.into())?;
encoder.emit_vec(&self.cert_data)?;
Ok(())
}
}
impl RecordDataDecodable<'_> for TLSA {
fn read_data(
decoder: &mut BinDecoder<'_>,
rdata_length: Restrict<u16>,
) -> Result<Self, DecodeError> {
let cert_usage = decoder.read_u8()?.unverified().into();
let selector = decoder.read_u8()?.unverified().into();
let matching = decoder.read_u8()?.unverified().into();
let cert_len = rdata_length
.map(|u| u as usize)
.checked_sub(3)
.map_err(|len| DecodeError::IncorrectRDataLengthRead { read: 3, len })?
.unverified();
let cert_data = decoder.read_vec(cert_len)?.unverified();
Ok(Self {
cert_usage,
selector,
matching,
cert_data,
})
}
}
impl RecordData for TLSA {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::TLSA(data) => Some(data),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::TLSA
}
fn into_rdata(self) -> RData {
RData::TLSA(self)
}
}
impl fmt::Display for TLSA {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{usage} {selector} {matching} {cert}",
usage = u8::from(self.cert_usage),
selector = u8::from(self.selector),
matching = u8::from(self.matching),
cert = sshfp::HEX.encode(&self.cert_data),
)
}
}
fn to_u8(data: &str) -> Result<u8, ParseError> {
data.parse().map_err(ParseError::from)
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
#[cfg(feature = "std")]
use std::println;
use super::*;
#[test]
fn read_cert_usage() {
assert_eq!(CertUsage::PkixTa, CertUsage::from(0));
assert_eq!(CertUsage::PkixEe, CertUsage::from(1));
assert_eq!(CertUsage::DaneTa, CertUsage::from(2));
assert_eq!(CertUsage::DaneEe, CertUsage::from(3));
assert_eq!(CertUsage::Unassigned(4), CertUsage::from(4));
assert_eq!(CertUsage::Unassigned(254), CertUsage::from(254));
assert_eq!(CertUsage::Private, CertUsage::from(255));
assert_eq!(u8::from(CertUsage::PkixTa), 0);
assert_eq!(u8::from(CertUsage::PkixEe), 1);
assert_eq!(u8::from(CertUsage::DaneTa), 2);
assert_eq!(u8::from(CertUsage::DaneEe), 3);
assert_eq!(u8::from(CertUsage::Unassigned(4)), 4);
assert_eq!(u8::from(CertUsage::Unassigned(254)), 254);
assert_eq!(u8::from(CertUsage::Private), 255);
}
#[test]
fn read_selector() {
assert_eq!(Selector::Full, Selector::from(0));
assert_eq!(Selector::Spki, Selector::from(1));
assert_eq!(Selector::Unassigned(2), Selector::from(2));
assert_eq!(Selector::Unassigned(254), Selector::from(254));
assert_eq!(Selector::Private, Selector::from(255));
assert_eq!(u8::from(Selector::Full), 0);
assert_eq!(u8::from(Selector::Spki), 1);
assert_eq!(u8::from(Selector::Unassigned(2)), 2);
assert_eq!(u8::from(Selector::Unassigned(254)), 254);
assert_eq!(u8::from(Selector::Private), 255);
}
#[test]
fn read_matching() {
assert_eq!(Matching::Raw, Matching::from(0));
assert_eq!(Matching::Sha256, Matching::from(1));
assert_eq!(Matching::Sha512, Matching::from(2));
assert_eq!(Matching::Unassigned(3), Matching::from(3));
assert_eq!(Matching::Unassigned(254), Matching::from(254));
assert_eq!(Matching::Private, Matching::from(255));
assert_eq!(u8::from(Matching::Raw), 0);
assert_eq!(u8::from(Matching::Sha256), 1);
assert_eq!(u8::from(Matching::Sha512), 2);
assert_eq!(u8::from(Matching::Unassigned(3)), 3);
assert_eq!(u8::from(Matching::Unassigned(254)), 254);
assert_eq!(u8::from(Matching::Private), 255);
}
fn test_encode_decode(rdata: TLSA) {
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
rdata.emit(&mut encoder).expect("failed to emit tlsa");
let bytes = encoder.into_bytes();
#[cfg(feature = "std")]
println!("bytes: {bytes:?}");
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata = TLSA::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("failed to read back");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_encode_decode_tlsa() {
test_encode_decode(TLSA::new(
CertUsage::PkixEe,
Selector::Spki,
Matching::Sha256,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
test_encode_decode(TLSA::new(
CertUsage::PkixTa,
Selector::Full,
Matching::Raw,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
test_encode_decode(TLSA::new(
CertUsage::DaneEe,
Selector::Full,
Matching::Sha512,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
test_encode_decode(TLSA::new(
CertUsage::Unassigned(40),
Selector::Unassigned(39),
Matching::Unassigned(6),
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
}
#[test]
fn test_parsing() {
assert!(
TLSA::from_tokens(
vec![
"0",
"0",
"1",
"d2abde240d7cd3ee6b4b28c54df034b9",
"7983a1d16e8a410e4561cb106618e971",
]
.into_iter()
)
.is_ok()
);
assert!(
TLSA::from_tokens(
vec![
"1",
"1",
"2",
"92003ba34942dc74152e2f2c408d29ec",
"a5a520e7f2e06bb944f4dca346baf63c",
"1b177615d466f6c4b71c216a50292bd5",
"8c9ebdd2f74e38fe51ffd48c43326cbc",
]
.into_iter()
)
.is_ok()
);
}
}