//! //! Structs and information to create/parse the _fenrir DNSSEC record //! //! Encoding and decoding in base85, RFC1924 //! //! Basic encoding idea: //! * 1 byte: divided in two: //! * half: num of addresses //! * half: num of pubkeys //! * 1 byte: divided in half: //! * half: number of key exchanges //! * half: number of Hkdfs //! * 1 byte: divided in half: //! * half: number of ciphers //! * half: nothing //! [ # list of pubkeys (max: 16) //! * 2 byte: pubkey id //! * 1 byte: pubkey length //! * 1 byte: pubkey type //! * Y bytes: pubkey //! ] //! [ # list of addresses //! * 1 byte: bitfield //! * 0..1 ipv4/ipv6 //! * 2..4 priority (for failover) //! * 5..7 weight between priority //! * 1 byte: divided in half: //! * half: num of public key indexes //! * half: num of handshake ids //! * 2 bytes: UDP port //! * [ HALF byte per public key idx ] (index on the list of public keys) //! * [ 1 byte per handshake id ] //! * X bytes: IP //! ] //! [ # list of supported key exchanges //! * 1 byte for each cipher //! ] //! [ # list of supported HDKFs //! * 1 byte for each hkdf //! ] //! [ # list of supported ciphers //! * 1 byte for each cipher //! ] use crate::{ connection::handshake::HandshakeID, enc::{ self, asym::{KeyExchangeKind, KeyID, PubKey}, hkdf::HkdfKind, sym::CipherKind, }, }; use ::core::num::NonZeroU16; use ::num_traits::FromPrimitive; use ::std::{net::IpAddr, vec::Vec}; /* * Public key data */ /// Public Key Index. /// this points to the index of the public key in the array of public keys. /// needed to have one byte less in the list of public keys /// supported by and address #[derive(Debug, Copy, Clone, PartialEq)] pub struct PubKeyIdx(pub u8); /* * Address data */ /// Priority of each group of addresses #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] // public enum: use non_exhaustive to force users to add a default case // so in the future we can expand this easily #[non_exhaustive] #[repr(u8)] pub enum AddressPriority { /// Initially contact addresses in this priority P1 = 0, /// First failover P2, /// Second failover P3, /// Third failover P4, /// Fourth failover P5, /// Fifth failover P6, /// Sisth failover P7, /// Seventh failover P8, } impl TryFrom<&str> for AddressPriority { type Error = ::std::io::Error; fn try_from(raw: &str) -> Result { if let Ok(priority_u8) = raw.parse::() { if priority_u8 >= 1 { if let Some(priority) = AddressPriority::from_u8(priority_u8 - 1) { return Ok(priority); } } } return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Priority must be between 1 and 8", )); } } /// Inside of each group, weight of the address /// This helps in distributing the traffic to multiple authentication servers: /// * client sums all weights in a group /// * generate a random number [0..sum_of_weights] /// * the number indicates which server will take the connection /// So to equally distribute all connections you just have to use the same /// weight in the same group #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] // public enum: use non_exhaustive to force users to add a default case // so in the future we can expand this easily #[non_exhaustive] #[repr(u8)] pub enum AddressWeight { /// Minimum weigth: 1 W1 = 0, /// little weigth: 2 W2, /// little weigth: 3 W3, /// medium weigth: 4 W4, /// medium weigth: 5 W5, /// heavy weigth: 6 W6, /// heavy weigth: 7 W7, /// Maximum weigth: 8 W8, } impl TryFrom<&str> for AddressWeight { type Error = ::std::io::Error; fn try_from(raw: &str) -> Result { if let Ok(weight_u8) = raw.parse::() { if weight_u8 >= 1 { if let Some(weight) = AddressWeight::from_u8(weight_u8 - 1) { return Ok(weight); } } } return Err(::std::io::Error::new( ::std::io::ErrorKind::InvalidData, "Weight must be between 1 and 8", )); } } /// Authentication server address information: /// * ip /// * udp port /// * priority /// * weight within priority /// * list of supported handshakes IDs /// * list of public keys. Indexes in the Record.public_keys #[derive(Debug, Clone)] pub struct Address { /// Ip address of server, v4 or v6 pub ip: IpAddr, /// udp port. None means that this address is reachable only /// with Fenrir over IP pub port: Option, /// Priority group of this address pub priority: AddressPriority, /// Weight of this address in the priority group pub weight: AddressWeight, /// List of supported handshakes pub handshake_ids: Vec, /// Public key IDs used by this address pub public_key_idx: Vec, } impl Address { /// return this Address as a socket address /// Note that since Fenrir can work on top of IP, without ports, /// this is not guaranteed to return a SocketAddr pub fn as_sockaddr(&self) -> Option<::std::net::SocketAddr> { match self.port { Some(port) => { Some(::std::net::SocketAddr::new(self.ip, port.get())) } None => None, } } fn len(&self) -> usize { let mut size = 4; let num_pubkey_idx = self.public_key_idx.len(); let idx_bytes = (num_pubkey_idx / 2) + (num_pubkey_idx % 2); size = size + idx_bytes + self.handshake_ids.len(); size + match self.ip { IpAddr::V4(_) => 4, IpAddr::V6(_) => 16, } } fn serialize_into(&self, raw: &mut [u8]) { let mut bitfield: u8 = match self.ip { IpAddr::V4(_) => 0, IpAddr::V6(_) => 1, }; bitfield <<= 3; bitfield |= self.priority as u8; bitfield <<= 3; bitfield |= self.weight as u8; raw[0] = bitfield; let len_combined: u8 = self.public_key_idx.len() as u8; let len_combined = len_combined << 4; let len_combined = len_combined | self.handshake_ids.len() as u8; raw[1] = len_combined; raw[2..4].copy_from_slice( &(match self.port { Some(port) => port.get().to_le_bytes(), None => [0, 0], // oh noez, which zero goes first? }), ); let mut written: usize = 4; // pair every idx, since the max is 16 for chunk in self.public_key_idx.chunks(2) { let second = { if chunk.len() == 2 { chunk[1].0 } else { 0 } }; let tmp = chunk[0].0 << 4; let tmp = tmp | second; raw[written] = tmp; written = written + 1; } for id in self.handshake_ids.iter() { raw[written] = *id as u8; written = written + 1; } let next_written; match self.ip { IpAddr::V4(ip) => { next_written = written + 4; let raw_ip = ip.octets(); raw[written..next_written].copy_from_slice(&raw_ip); } IpAddr::V6(ip) => { next_written = written + 16; let raw_ip = ip.octets(); raw[written..next_written].copy_from_slice(&raw_ip); } }; assert!( next_written == raw.len(), "write how much? {} - {}", next_written, raw.len() ); } fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> { if raw.len() < 9 { return Err(Error::NotEnoughData(0)); } // 3-byte bitfield let ip_type = raw[0] >> 6; let is_ipv6: bool; let ip_len: usize; match ip_type { 0 => { is_ipv6 = false; ip_len = 4; } 1 => { is_ipv6 = true; ip_len = 16; } _ => return Err(Error::UnsupportedData(0)), } let raw_priority = (raw[0] << 2) >> 5; let raw_weight = (raw[0] << 5) >> 5; let priority = AddressPriority::from_u8(raw_priority).unwrap(); let weight = AddressWeight::from_u8(raw_weight).unwrap(); // Add publickey ids let num_pubkey_idx = (raw[1] >> 4) as usize; let num_handshake_ids = (raw[1] & 0x0F) as usize; // UDP port let raw_port = u16::from_le_bytes([raw[2], raw[3]]); let port = if raw_port == 0 { None } else { Some(NonZeroU16::new(raw_port).unwrap()) }; if raw.len() <= 3 + num_pubkey_idx + num_handshake_ids + ip_len { return Err(Error::NotEnoughData(3)); } let mut bytes_parsed = 4; let mut public_key_idx = Vec::with_capacity(num_pubkey_idx); let idx_bytes = (num_pubkey_idx / 2) + (num_pubkey_idx % 2); let mut idx_added = 0; for raw_pubkey_idx_pair in raw[bytes_parsed..(bytes_parsed + idx_bytes)].iter() { let first = PubKeyIdx(raw_pubkey_idx_pair >> 4); let second = PubKeyIdx(raw_pubkey_idx_pair & 0x0F); public_key_idx.push(first); if num_pubkey_idx - idx_added >= 2 { public_key_idx.push(second); } idx_added = idx_added + 2; } bytes_parsed = bytes_parsed + idx_bytes; // add handshake ids let mut handshake_ids = Vec::with_capacity(num_handshake_ids); for raw_handshake_id in raw[bytes_parsed..(bytes_parsed + num_handshake_ids)].iter() { match HandshakeID::from_u8(*raw_handshake_id) { Some(h_id) => handshake_ids.push(h_id), None => { ::tracing::warn!( "Unsupported handshake {}. Upgrade?", *raw_handshake_id ); // ignore unsupported handshakes } } } bytes_parsed = bytes_parsed + num_handshake_ids; let ip = if is_ipv6 { let ip_end = bytes_parsed + 16; if raw.len() < ip_end { return Err(Error::NotEnoughData(bytes_parsed)); } let raw_ip: [u8; 16] = raw[bytes_parsed..ip_end].try_into().unwrap(); bytes_parsed = bytes_parsed + 16; IpAddr::from(raw_ip) } else { let ip_end = bytes_parsed + 4; if raw.len() < ip_end { return Err(Error::NotEnoughData(bytes_parsed)); } let raw_ip: [u8; 4] = raw[bytes_parsed..ip_end].try_into().unwrap(); bytes_parsed = bytes_parsed + 4; IpAddr::from(raw_ip) }; Ok(( Self { ip, port, priority, weight, public_key_idx, handshake_ids, }, bytes_parsed, )) } } /* * Actual record putting it all toghether */ /// All informations found in the DNSSEC record #[derive(Debug, Clone)] pub struct Record { /// Public keys used by any authentication server pub public_keys: Vec<(KeyID, PubKey)>, /// List of all authentication servers' addresses. /// Multiple ones can point to the same authentication server pub addresses: Vec
, /// List of supported key exchanges pub key_exchanges: Vec, /// List of supported key exchanges pub hkdfs: Vec, /// List of supported ciphers pub ciphers: Vec, } impl Record { /// Simply encode all the record in base85 pub fn encode(&self) -> Result { // check possible failure scenarios if self.public_keys.len() == 0 { return Err(Error::NoPublicKeyFound); } if self.public_keys.len() > 16 { return Err(Error::Max16PublicKeys); } if self.addresses.len() == 0 { return Err(Error::NoAddressFound); } if self.addresses.len() > 16 { return Err(Error::Max16Addresses); } if self.key_exchanges.len() > 16 { return Err(Error::Max16KeyExchanges); } if self.hkdfs.len() > 16 { return Err(Error::Max16Hkdfs); } if self.ciphers.len() > 16 { return Err(Error::Max16Ciphers); } // everything else is all good let total_size: usize = 3 + self.addresses.iter().map(|a| a.len()).sum::() + self .public_keys .iter() .map(|(_, key)| 3 + key.kind().pub_len()) .sum::() + self.key_exchanges.len() + self.hkdfs.len() + self.ciphers.len(); let mut raw = Vec::with_capacity(total_size); raw.resize(total_size, 0); // amount of data. addresses, then pubkeys. 4 bits each let len_combined: u8 = self.addresses.len() as u8; let len_combined = len_combined << 4; let len_combined = len_combined | self.public_keys.len() as u8; raw[0] = len_combined; // number of key exchanges and hkdfs let len_combined: u8 = self.key_exchanges.len() as u8; let len_combined = len_combined << 4; let len_combined = len_combined | self.hkdfs.len() as u8; raw[1] = len_combined; let num_of_ciphers: u8 = (self.ciphers.len() as u8) << 4; raw[2] = num_of_ciphers; let mut written: usize = 3; for (public_key_id, public_key) in self.public_keys.iter() { let key_id_bytes = public_key_id.0.to_le_bytes(); let written_next = written + KeyID::len(); raw[written..written_next].copy_from_slice(&key_id_bytes); written = written_next; raw[written] = public_key.len() as u8; written = written + 1; let written_next = written + public_key.len(); public_key.serialize_into(&mut raw[written..written_next]); written = written_next; } for address in self.addresses.iter() { let len = address.len(); let written_next = written + len; address.serialize_into(&mut raw[written..written_next]); written = written_next; } for k_x in self.key_exchanges.iter() { raw[written] = *k_x as u8; written = written + 1; } for h in self.hkdfs.iter() { raw[written] = *h as u8; written = written + 1; } for c in self.ciphers.iter() { raw[written] = *c as u8; written = written + 1; } Ok(::base85::encode(&raw)) } /// Decode from base85 to the actual object pub fn decode(raw: &[u8]) -> Result { // bare minimum for lengths, (1 address), (1 key),no cipher negotiation const MIN_RAW_LENGTH: usize = 3 + (6 + 4) + (4 + 32); if raw.len() <= MIN_RAW_LENGTH { return Err(Error::NotEnoughData(0)); } let mut num_addresses = (raw[0] >> 4) as usize; let mut num_public_keys = (raw[0] & 0x0F) as usize; let mut num_key_exchanges = (raw[1] >> 4) as usize; let mut num_hkdfs = (raw[1] & 0x0F) as usize; let mut num_ciphers = (raw[2] >> 4) as usize; let mut bytes_parsed = 3; let mut result = Self { addresses: Vec::with_capacity(num_addresses), public_keys: Vec::with_capacity(num_public_keys), key_exchanges: Vec::with_capacity(num_key_exchanges), hkdfs: Vec::with_capacity(num_hkdfs), ciphers: Vec::with_capacity(num_ciphers), }; while num_public_keys > 0 { if bytes_parsed + 3 >= raw.len() { return Err(Error::NotEnoughData(bytes_parsed)); } let raw_key_id = u16::from_le_bytes([raw[bytes_parsed], raw[bytes_parsed + 1]]); let id = KeyID(raw_key_id); bytes_parsed = bytes_parsed + KeyID::len(); let pubkey_length = raw[bytes_parsed] as usize; bytes_parsed = bytes_parsed + 1; let bytes_next_key = bytes_parsed + pubkey_length; if bytes_next_key > raw.len() { return Err(Error::NotEnoughData(bytes_parsed)); } let (public_key, bytes) = match PubKey::deserialize(&raw[bytes_parsed..bytes_next_key]) { Ok((public_key, bytes)) => (public_key, bytes), Err(enc::Error::UnsupportedKey(_)) => { // continue parsing. This could be a new pubkey type // that is not supported by an older client bytes_parsed = bytes_parsed + pubkey_length; continue; } Err(_) => { return Err(Error::UnsupportedData(bytes_parsed)); } }; if bytes != pubkey_length { return Err(Error::UnsupportedData(bytes_parsed)); } bytes_parsed = bytes_parsed + bytes; result.public_keys.push((id, public_key)); num_public_keys = num_public_keys - 1; } while num_addresses > 0 { let (address, bytes) = match Address::decode_raw(&raw[bytes_parsed..]) { Ok(address) => address, Err(Error::UnsupportedData(b)) => { return Err(Error::UnsupportedData(bytes_parsed + b)) } Err(Error::NotEnoughData(b)) => { return Err(Error::NotEnoughData(bytes_parsed + b)) } Err(e) => return Err(e), }; bytes_parsed = bytes_parsed + bytes; result.addresses.push(address); num_addresses = num_addresses - 1; } for addr in result.addresses.iter() { for idx in addr.public_key_idx.iter() { if idx.0 as usize >= result.public_keys.len() { return Err(Error::Max16PublicKeys); } if !result.public_keys[idx.0 as usize] .1 .kind() .capabilities() .has_exchange() { return Err(Error::UnsupportedData(bytes_parsed)); } } } if bytes_parsed + num_key_exchanges + num_hkdfs + num_ciphers != raw.len() { return Err(Error::NotEnoughData(bytes_parsed)); } while num_key_exchanges > 0 { let key_exchange = match KeyExchangeKind::from_u8(raw[bytes_parsed]) { Some(key_exchange) => key_exchange, None => { // continue parsing. This could be a new key exchange type // that is not supported by an older client ::tracing::warn!( "Unknown Key exchange {}. Ignoring", raw[bytes_parsed] ); bytes_parsed = bytes_parsed + 1; continue; } }; bytes_parsed = bytes_parsed + 1; result.key_exchanges.push(key_exchange); num_key_exchanges = num_key_exchanges - 1; } while num_hkdfs > 0 { let hkdf = match HkdfKind::from_u8(raw[bytes_parsed]) { Some(hkdf) => hkdf, None => { // continue parsing. This could be a new hkdf type // that is not supported by an older client ::tracing::warn!( "Unknown hkdf {}. Ignoring", raw[bytes_parsed] ); bytes_parsed = bytes_parsed + 1; continue; } }; bytes_parsed = bytes_parsed + 1; result.hkdfs.push(hkdf); num_hkdfs = num_hkdfs - 1; } while num_ciphers > 0 { let cipher = match CipherKind::from_u8(raw[bytes_parsed]) { Some(cipher) => cipher, None => { // continue parsing. This could be a new cipher type // that is not supported by an older client ::tracing::warn!( "Unknown Cipher {}. Ignoring", raw[bytes_parsed] ); bytes_parsed = bytes_parsed + 1; continue; } }; bytes_parsed = bytes_parsed + 1; result.ciphers.push(cipher); num_ciphers = num_ciphers - 1; } if bytes_parsed != raw.len() { Err(Error::UnknownData(bytes_parsed)) } else { Ok(result) } } } /// Possible errors in encoding or decoding the DNSSEC record #[derive(::thiserror::Error, Debug)] pub enum Error { /// General IO error #[error("IO error: {0:?}")] IO(#[from] ::std::io::Error), /// We need at least one address #[error("no addresses found")] NoAddressFound, /// Too many addresses (max 16) #[error("can't encode more than 16 addresses")] Max16Addresses, /// Too many key exchanges (max 16) #[error("can't encode more than 16 key exchanges")] Max16KeyExchanges, /// Too many Hkdfs (max 16) #[error("can't encode more than 16 Hkdfs")] Max16Hkdfs, /// Too many ciphers (max 16) #[error("can't encode more than 16 Ciphers")] Max16Ciphers, /// We need at least one public key #[error("no public keys found")] NoPublicKeyFound, /// Maximum 16 public keys supported #[error("can't encode more than 16 public keys")] Max16PublicKeys, /// Not enough data to decode something meaningful #[error("not enough data. Parsed {0} bytes")] NotEnoughData(usize), /// Unsupported Data: can't parse #[error("Unsupported data. Parsed {0} bytes")] UnsupportedData(usize), /// Unknown data at the end #[error("Unknown data after {0} bytes")] UnknownData(usize), }