1use bytes::BufMut;
12use nom::{IResult, bytes::streaming::take, number::streaming::be_u32};
13
14use crate::core::signature::SignatureFields;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct ResponseRecord {
19 pub signature_fields: SignatureFields,
22 pub dns: Vec<u8>,
24 pub cert: Vec<u8>,
26}
27
28impl ResponseRecord {
29 pub fn new(signature_fields: SignatureFields, dns: Vec<u8>, cert: Vec<u8>) -> Self {
30 Self {
31 signature_fields,
32 dns,
33 cert,
34 }
35 }
36
37 pub fn unsigned(dns: Vec<u8>, cert: Vec<u8>) -> Self {
38 Self::new(SignatureFields::empty(), dns, cert)
39 }
40
41 pub fn cert_fingerprint_hex(&self) -> Option<String> {
44 if self.cert.is_empty() {
45 return None;
46 }
47 use ring::digest::{SHA256, digest};
48 let digest = digest(&SHA256, &self.cert);
49 Some(digest.as_ref().iter().map(|b| format!("{b:02x}")).collect())
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct MultiResponse {
56 pub records: Vec<ResponseRecord>,
57}
58
59impl MultiResponse {
60 pub fn new(iter: impl IntoIterator<Item = ResponseRecord>) -> Self {
61 Self {
62 records: iter.into_iter().collect(),
63 }
64 }
65
66 pub fn encoding_size(&self) -> usize {
67 4 + self
68 .records
69 .iter()
70 .map(|record| {
71 4 + record.signature_fields.content_digest.len()
72 + 4
73 + record.signature_fields.signature_input.len()
74 + 4
75 + record.signature_fields.signature.len()
76 + 4
77 + record.dns.len()
78 + 4
79 + record.cert.len()
80 })
81 .sum::<usize>()
82 }
83
84 pub fn encode(&self) -> Vec<u8> {
85 let mut buf = Vec::with_capacity(self.encoding_size());
86 buf.put_multi_response(self);
87 buf
88 }
89}
90
91pub trait WriteMultiResponse {
92 fn put_multi_response(&mut self, response: &MultiResponse);
93}
94
95impl<B: BufMut> WriteMultiResponse for B {
96 fn put_multi_response(&mut self, response: &MultiResponse) {
97 self.put_u32(response.records.len() as u32);
98 for record in &response.records {
99 put_field(self, &record.signature_fields.content_digest);
100 put_field(self, &record.signature_fields.signature_input);
101 put_field(self, &record.signature_fields.signature);
102 put_field(self, &record.dns);
103 put_field(self, &record.cert);
104 }
105 }
106}
107
108fn put_field<B: BufMut>(buf: &mut B, value: &[u8]) {
109 buf.put_u32(value.len() as u32);
110 buf.put_slice(value);
111}
112
113pub fn be_multi_response(input: &[u8]) -> IResult<&[u8], MultiResponse> {
114 let (mut input, count) = be_u32(input)?;
115 let mut records = Vec::with_capacity(count as usize);
116 for _ in 0..count {
117 let (rest, content_digest) = be_field(input)?;
118 let (rest, signature_input) = be_field(rest)?;
119 let (rest, signature) = be_field(rest)?;
120 let (rest, dns) = be_field(rest)?;
121 let (rest, cert) = be_field(rest)?;
122 records.push(ResponseRecord::new(
123 SignatureFields {
124 content_digest,
125 signature_input,
126 signature,
127 },
128 dns,
129 cert,
130 ));
131 input = rest;
132 }
133 Ok((input, MultiResponse { records }))
134}
135
136fn be_field(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
137 let (input, len) = be_u32(input)?;
138 let (input, value) = take(len as usize)(input)?;
139 Ok((input, value.to_vec()))
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn multi_response_roundtrips() {
148 let response = MultiResponse::new([
149 ResponseRecord::new(
150 SignatureFields {
151 content_digest: b"sha-256=:abc:".to_vec(),
152 signature_input:
153 b"dns=(\"content-digest\");created=1;keyid=\"sha256:abc\";alg=\"ed25519\""
154 .to_vec(),
155 signature: b"dns=:sig:".to_vec(),
156 },
157 vec![1, 2, 3],
158 vec![4, 5],
159 ),
160 ResponseRecord::unsigned(vec![6, 7, 8, 9], Vec::new()),
161 ]);
162 let encoded = response.encode();
163 let (remain, decoded) = be_multi_response(&encoded).unwrap();
164 assert!(remain.is_empty());
165 assert_eq!(decoded, response);
166 }
167}