#![allow(clippy::use_self)]
use std::fmt;
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use super::sshfp;
use crate::error::*;
use crate::serialize::binary::*;
#[cfg_attr(feature = "serde-config", 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-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum CertUsage {
CA,
Service,
TrustAnchor,
DomainIssued,
Unassigned(u8),
Private,
}
impl From<u8> for CertUsage {
fn from(usage: u8) -> Self {
match usage {
0 => Self::CA,
1 => Self::Service,
2 => Self::TrustAnchor,
3 => Self::DomainIssued,
4..=254 => Self::Unassigned(usage),
255 => Self::Private,
}
}
}
impl From<CertUsage> for u8 {
fn from(usage: CertUsage) -> Self {
match usage {
CertUsage::CA => 0,
CertUsage::Service => 1,
CertUsage::TrustAnchor => 2,
CertUsage::DomainIssued => 3,
CertUsage::Unassigned(usage) => usage,
CertUsage::Private => 255,
}
}
}
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Selector {
Full,
Spki,
Unassigned(u8),
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-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Matching {
Raw,
Sha256,
Sha512,
Unassigned(u8),
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
}
}
pub fn read(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(TLSA {
cert_usage,
selector,
matching,
cert_data,
})
}
pub fn emit(encoder: &mut BinEncoder<'_>, tlsa: &TLSA) -> ProtoResult<()> {
encoder.emit_u8(tlsa.cert_usage.into())?;
encoder.emit_u8(tlsa.selector.into())?;
encoder.emit_u8(tlsa.matching.into())?;
encoder.emit_vec(&tlsa.cert_data)?;
Ok(())
}
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)]
use super::*;
#[test]
fn read_cert_usage() {
assert_eq!(CertUsage::CA, CertUsage::from(0));
assert_eq!(CertUsage::Service, CertUsage::from(1));
assert_eq!(CertUsage::TrustAnchor, CertUsage::from(2));
assert_eq!(CertUsage::DomainIssued, 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::CA), 0);
assert_eq!(u8::from(CertUsage::Service), 1);
assert_eq!(u8::from(CertUsage::TrustAnchor), 2);
assert_eq!(u8::from(CertUsage::DomainIssued), 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);
emit(&mut encoder, &rdata).expect("failed to emit tlsa");
let bytes = encoder.into_bytes();
println!("bytes: {:?}", bytes);
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata =
read(&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::Service,
Selector::Spki,
Matching::Sha256,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
test_encode_decode(TLSA::new(
CertUsage::CA,
Selector::Full,
Matching::Raw,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
test_encode_decode(TLSA::new(
CertUsage::DomainIssued,
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],
));
}
}