#![allow(clippy::use_self)]
use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::sshfp;
use crate::{
error::{ProtoError, ProtoResult},
rr::{RData, RecordData, RecordDataDecodable, RecordType},
serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath},
};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct TLSA {
cert_usage: CertUsage,
selector: Selector,
matching: Matching,
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 fn cert_usage(&self) -> CertUsage {
self.cert_usage
}
pub fn selector(&self) -> Selector {
self.selector
}
pub fn matching(&self) -> Matching {
self.matching
}
pub fn cert_data(&self) -> &[u8] {
&self.cert_data
}
}
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>) -> ProtoResult<TLSA> {
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(|_| ProtoError::from("invalid rdata length in TLSA"))?
.unverified();
let cert_data = decoder.read_vec(cert_len)?.unverified();
Ok(Self {
cert_usage,
selector,
matching,
cert_data,
})
}
}
impl RecordData for TLSA {
fn try_from_rdata(data: RData) -> Result<Self, RData> {
match data {
RData::TLSA(data) => Ok(data),
_ => Err(data),
}
}
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),
)
}
}
#[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],
));
}
}