1use std::borrow::Borrow;
12
13use bytes::BufMut;
14use dhttp_identity::certificate::CertificateChainKey;
15use nom::{IResult, bytes::streaming::take, number::streaming::be_u32};
16use rustls::pki_types::CertificateDer;
17
18use crate::core::signature::SignatureFields;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ResponseRecord {
23 pub signature_fields: SignatureFields,
26 pub dns: Vec<u8>,
28 pub cert: Vec<u8>,
30}
31
32impl ResponseRecord {
33 pub fn new(signature_fields: SignatureFields, dns: Vec<u8>, cert: Vec<u8>) -> Self {
34 Self {
35 signature_fields,
36 dns,
37 cert,
38 }
39 }
40
41 pub fn unsigned(dns: Vec<u8>, cert: Vec<u8>) -> Self {
42 Self::new(SignatureFields::empty(), dns, cert)
43 }
44
45 pub fn cert_fingerprint_hex(&self) -> Option<String> {
48 if self.cert.is_empty() {
49 return None;
50 }
51 use ring::digest::{SHA256, digest};
52 let digest = digest(&SHA256, &self.cert);
53 Some(digest.as_ref().iter().map(|b| format!("{b:02x}")).collect())
54 }
55
56 pub fn publisher_certificate_chain_key(&self) -> Option<CertificateChainKey> {
57 if self.cert.is_empty() {
58 return None;
59 }
60
61 let cert = CertificateDer::from(self.cert.clone());
62 dhttp_identity::identity::extract_dhttp_subject_key_identifier(std::slice::from_ref(&cert))
63 .ok()
64 .map(|ski| ski.chain().clone())
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct MultiResponse {
71 pub records: Vec<ResponseRecord>,
72}
73
74impl MultiResponse {
75 pub fn new(iter: impl IntoIterator<Item = ResponseRecord>) -> Self {
76 Self {
77 records: iter.into_iter().collect(),
78 }
79 }
80
81 pub fn encoding_size(&self) -> usize {
82 4 + self
83 .records
84 .iter()
85 .map(|record| {
86 4 + record.signature_fields.content_digest.len()
87 + 4
88 + record.signature_fields.signature_input.len()
89 + 4
90 + record.signature_fields.signature.len()
91 + 4
92 + record.dns.len()
93 + 4
94 + record.cert.len()
95 })
96 .sum::<usize>()
97 }
98
99 pub fn encode(&self) -> Vec<u8> {
100 let mut buf = Vec::with_capacity(self.encoding_size());
101 buf.put_multi_response(self);
102 buf
103 }
104
105 pub fn encode_records<I, R>(records: I) -> Vec<u8>
106 where
107 I: IntoIterator<Item = R>,
108 R: Borrow<ResponseRecord>,
109 {
110 let mut buf = Vec::new();
111 buf.put_u32(0);
112
113 let mut count = 0u32;
114 for record in records {
115 count = count
116 .checked_add(1)
117 .expect("multi response record count exceeds u32 range");
118 put_response_record(&mut buf, record.borrow());
119 }
120
121 buf[..4].copy_from_slice(&count.to_be_bytes());
122 buf
123 }
124}
125
126pub trait WriteMultiResponse {
127 fn put_multi_response(&mut self, response: &MultiResponse);
128}
129
130impl<B: BufMut> WriteMultiResponse for B {
131 fn put_multi_response(&mut self, response: &MultiResponse) {
132 self.put_u32(response.records.len() as u32);
133 for record in &response.records {
134 put_response_record(self, record);
135 }
136 }
137}
138
139fn put_response_record<B: BufMut>(buf: &mut B, record: &ResponseRecord) {
140 put_field(buf, &record.signature_fields.content_digest);
141 put_field(buf, &record.signature_fields.signature_input);
142 put_field(buf, &record.signature_fields.signature);
143 put_field(buf, &record.dns);
144 put_field(buf, &record.cert);
145}
146
147fn put_field<B: BufMut>(buf: &mut B, value: &[u8]) {
148 buf.put_u32(value.len() as u32);
149 buf.put_slice(value);
150}
151
152pub fn be_multi_response(input: &[u8]) -> IResult<&[u8], MultiResponse> {
153 let (mut input, count) = be_u32(input)?;
154 let mut records = Vec::with_capacity(count as usize);
155 for _ in 0..count {
156 let (rest, content_digest) = be_field(input)?;
157 let (rest, signature_input) = be_field(rest)?;
158 let (rest, signature) = be_field(rest)?;
159 let (rest, dns) = be_field(rest)?;
160 let (rest, cert) = be_field(rest)?;
161 records.push(ResponseRecord::new(
162 SignatureFields {
163 content_digest,
164 signature_input,
165 signature,
166 },
167 dns,
168 cert,
169 ));
170 input = rest;
171 }
172 Ok((input, MultiResponse { records }))
173}
174
175fn be_field(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
176 let (input, len) = be_u32(input)?;
177 let (input, value) = take(len as usize)(input)?;
178 Ok((input, value.to_vec()))
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn multi_response_roundtrips() {
187 let response = MultiResponse::new([
188 ResponseRecord::new(
189 SignatureFields {
190 content_digest: b"sha-256=:abc:".to_vec(),
191 signature_input:
192 b"dns=(\"content-digest\");created=1;keyid=\"sha256:abc\";alg=\"ed25519\""
193 .to_vec(),
194 signature: b"dns=:sig:".to_vec(),
195 },
196 vec![1, 2, 3],
197 vec![4, 5],
198 ),
199 ResponseRecord::unsigned(vec![6, 7, 8, 9], Vec::new()),
200 ]);
201 let encoded = response.encode();
202 let (remain, decoded) = be_multi_response(&encoded).unwrap();
203 assert!(remain.is_empty());
204 assert_eq!(decoded, response);
205 }
206
207 #[test]
208 fn encode_records_matches_multi_response_encoding_for_owned_records() {
209 let records = [
210 ResponseRecord::unsigned(vec![1, 2, 3], vec![4, 5]),
211 ResponseRecord::new(
212 SignatureFields {
213 content_digest: b"sha-256=:abc:".to_vec(),
214 signature_input: b"dns=(\"content-digest\")".to_vec(),
215 signature: b"dns=:sig:".to_vec(),
216 },
217 vec![6, 7],
218 Vec::new(),
219 ),
220 ];
221 let response = MultiResponse::new(records.clone());
222
223 assert_eq!(MultiResponse::encode_records(records), response.encode());
224 }
225
226 #[test]
227 fn encode_records_matches_multi_response_encoding_for_borrowed_records() {
228 let response = MultiResponse::new([
229 ResponseRecord::unsigned(vec![1, 2, 3], vec![4, 5]),
230 ResponseRecord::unsigned(vec![6, 7], Vec::new()),
231 ]);
232
233 assert_eq!(
234 MultiResponse::encode_records(response.records.iter()),
235 response.encode()
236 );
237 }
238}