1use crate::cbor::{self, Value};
7use crate::node_key::{node_id_of, NodeKey};
8use crate::profile::Profile;
9use crate::signed_object::{sign_object, verify_object, Object};
10
11use super::sealed::{clear_or_sealed, sealed_field, Sealed, SealedContext};
12use super::{
13 bounded_text, check_payload, entry, fixed, has_fields, identity_signer, names_request,
14 object_refusal, read_fields, received_frame, text_of, FrameError, Rule, VerifiedRequest,
15 MAX_ERROR_CODE_BYTES, MAX_ERROR_TEXT_BYTES, PROTOCOL_VERSION, RELAY_ERROR_LABEL, REPLY_LABEL,
16};
17
18const RESULT: &str = "result";
19const ERROR: &str = "error";
20const STREAM_ERROR: &str = "stream_error";
21
22const RELAY_CODES: &[&str] = &["unknown_next_peer"];
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ReplyType {
28 Result,
29 Error,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum RelayErrorType {
35 Error,
36 StreamError,
37}
38
39impl RelayErrorType {
40 fn name(self) -> &'static str {
41 match self {
42 RelayErrorType::Error => ERROR,
43 RelayErrorType::StreamError => STREAM_ERROR,
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq)]
52pub struct VerifiedReply {
53 pub frame_type: ReplyType,
54 pub responded_by: [u8; 32],
55 pub payload: Option<Value>,
56 pub code: Option<String>,
57 pub detail: Option<String>,
58 pub sealed: Option<Sealed>,
59}
60
61#[derive(Debug, Clone, PartialEq)]
65pub struct RelayErrorSpec {
66 pub frame_type: RelayErrorType,
67 pub request: VerifiedRequest,
68 pub code: String,
69 pub offending_hop: Option<[u8; 32]>,
70 pub source_route_partial: Option<Vec<u8>>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct VerifiedRelayError {
77 pub frame_type: RelayErrorType,
78 pub reported_by: [u8; 32],
79 pub code: String,
80 pub offending_hop: Option<[u8; 32]>,
81}
82
83pub fn sign_result(
87 request: &VerifiedRequest,
88 payload: &Value,
89 source_route_reverse: Option<Vec<u8>>,
90 key: &NodeKey,
91) -> Result<Value, FrameError> {
92 reply_signer(request, key)?;
93 check_payload(payload)?;
94 sign_reply(
95 ReplyType::Result,
96 request,
97 vec![entry("payload", payload.clone())],
98 source_route_reverse,
99 key,
100 )
101}
102
103pub fn sign_provider_error(
106 request: &VerifiedRequest,
107 code: &str,
108 detail: Option<&str>,
109 source_route_reverse: Option<Vec<u8>>,
110 key: &NodeKey,
111) -> Result<Value, FrameError> {
112 reply_signer(request, key)?;
113 bounded_text("code", code.as_bytes(), MAX_ERROR_CODE_BYTES)?;
114 let mut fields = vec![entry("code", Value::text(code))];
115 if let Some(detail) = detail {
116 bounded_text("detail", detail.as_bytes(), MAX_ERROR_TEXT_BYTES)?;
117 fields.push(entry("detail", Value::text(detail)));
118 }
119 sign_reply(ReplyType::Error, request, fields, source_route_reverse, key)
120}
121
122pub fn sign_sealed_result(
125 request: &VerifiedRequest,
126 sealed: &Sealed,
127 source_route_reverse: Option<Vec<u8>>,
128 key: &NodeKey,
129) -> Result<Value, FrameError> {
130 sign_sealed_reply(
131 ReplyType::Result,
132 request,
133 sealed,
134 source_route_reverse,
135 key,
136 )
137}
138
139pub fn sign_sealed_provider_error(
142 request: &VerifiedRequest,
143 sealed: &Sealed,
144 source_route_reverse: Option<Vec<u8>>,
145 key: &NodeKey,
146) -> Result<Value, FrameError> {
147 sign_sealed_reply(ReplyType::Error, request, sealed, source_route_reverse, key)
148}
149
150fn sign_sealed_reply(
151 frame_type: ReplyType,
152 request: &VerifiedRequest,
153 sealed: &Sealed,
154 source_route_reverse: Option<Vec<u8>>,
155 key: &NodeKey,
156) -> Result<Value, FrameError> {
157 reply_signer(request, key)?;
158 if !sealed.shaped(SealedContext::Reply) {
159 return Err(FrameError::SealedShape);
160 }
161 sign_reply(
162 frame_type,
163 request,
164 vec![entry("sealed", sealed.value())],
165 source_route_reverse,
166 key,
167 )
168}
169
170fn reply_signer(request: &VerifiedRequest, key: &NodeKey) -> Result<(), FrameError> {
171 identity_signer(key)?;
172 if key.key_id() != request.target {
173 return Err(FrameError::Unsignable);
174 }
175 Ok(())
176}
177
178fn sign_reply(
179 frame_type: ReplyType,
180 request: &VerifiedRequest,
181 mut fields: Vec<(Value, Value)>,
182 source_route_reverse: Option<Vec<u8>>,
183 key: &NodeKey,
184) -> Result<Value, FrameError> {
185 let name = match frame_type {
186 ReplyType::Result => RESULT,
187 ReplyType::Error => ERROR,
188 };
189 fields.extend([
190 entry("frame_type", Value::text(name)),
191 entry("request_id", Value::Bytes(request.request_id.to_vec())),
192 entry("request_hash", Value::Bytes(request.request_hash.to_vec())),
193 entry("responded_by", Value::Bytes(key.key_id().to_vec())),
194 ]);
195 let reply = sign_object(REPLY_LABEL, &fields, key).map_err(object_refusal)?;
196 Ok(routed_frame(
197 name,
198 "reply",
199 &reply,
200 "source_route_reverse",
201 source_route_reverse,
202 ))
203}
204
205const REPLY_ROUTES: &[(&str, Rule)] = &[("source_route_reverse", Rule::AnyBytes)];
206const RELAY_ERROR_ROUTES: &[(&str, Rule)] = &[("source_route_partial", Rule::AnyBytes)];
207
208pub fn verify_reply(
213 frame: &Value,
214 request: &VerifiedRequest,
215 profile: Profile,
216) -> Result<VerifiedReply, FrameError> {
217 let (frame_type, object) = received_frame(
218 frame,
219 "reply",
220 Rule::CarriedObject,
221 REPLY_ROUTES,
222 &[RESULT, ERROR],
223 )
224 .ok_or(FrameError::Malformed)?;
225 let verified = verify_object(REPLY_LABEL, &object, profile).map_err(object_refusal)?;
226 let fields =
227 read_fields(&verified.fields, &reply_table(&frame_type)).ok_or(FrameError::Malformed)?;
228 let (has_payload, has_code, has_detail, has_sealed) = (
229 fields.contains_key("payload"),
230 fields.contains_key("code"),
231 fields.contains_key("detail"),
232 fields.contains_key("sealed"),
233 );
234 let shaped = match (frame_type == RESULT, has_sealed) {
235 (true, _) => clear_or_sealed(&fields, "payload") && !has_code && !has_detail,
236 (false, true) => !has_code && !has_detail && !has_payload,
237 (false, false) => has_code && !has_payload,
238 };
239 if !has_fields(
240 &fields,
241 &["frame_type", "request_id", "request_hash", "responded_by"],
242 ) || !shaped
243 {
244 return Err(FrameError::Malformed);
245 }
246 let reply = VerifiedReply {
247 frame_type: if frame_type == RESULT {
248 ReplyType::Result
249 } else {
250 ReplyType::Error
251 },
252 responded_by: fixed(&fields["responded_by"]),
253 payload: fields.get("payload").cloned(),
254 code: fields.get("code").map(text_of),
255 detail: fields.get("detail").map(text_of),
256 sealed: sealed_field(&fields, SealedContext::Reply),
257 };
258 if reply.responded_by != node_id_of(&verified.key, profile) {
259 return Err(FrameError::KeyIdMismatch);
260 }
261 if !names_request(&fields, request) {
262 return Err(FrameError::RequestMismatch);
263 }
264 if reply.responded_by != request.target {
265 return Err(FrameError::NotTheTarget);
266 }
267 Ok(reply)
268}
269
270fn reply_table(frame_type: &str) -> Vec<(&'static str, Rule)> {
271 vec![
272 (
273 "frame_type",
274 Rule::TextIn(if frame_type == RESULT {
275 &[RESULT]
276 } else {
277 &[ERROR]
278 }),
279 ),
280 ("alg", Rule::Any),
281 ("request_id", Rule::BytesOf(16)),
282 ("request_hash", Rule::BytesOf(48)),
283 ("responded_by", Rule::BytesOf(32)),
284 ("payload", Rule::Any),
285 ("code", Rule::TextWithin(MAX_ERROR_CODE_BYTES)),
286 ("detail", Rule::TextWithin(MAX_ERROR_TEXT_BYTES)),
287 ("sealed", Rule::Sealed(SealedContext::Reply)),
288 ]
289}
290
291pub fn sign_relay_error(spec: &RelayErrorSpec, key: &NodeKey) -> Result<Value, FrameError> {
295 identity_signer(key)?;
296 if !RELAY_CODES.contains(&spec.code.as_str()) {
297 return Err(FrameError::RelayCodeOutsideItsSet);
298 }
299 let name = spec.frame_type.name();
300 let mut fields = vec![
301 entry("frame_type", Value::text(name)),
302 entry("request_id", Value::Bytes(spec.request.request_id.to_vec())),
303 entry(
304 "request_hash",
305 Value::Bytes(spec.request.request_hash.to_vec()),
306 ),
307 entry("reported_by", Value::Bytes(key.key_id().to_vec())),
308 entry("code", Value::text(spec.code.clone())),
309 ];
310 if let Some(hop) = spec.offending_hop {
311 fields.push(entry("offending_hop", Value::Bytes(hop.to_vec())));
312 }
313 let relay_error = sign_object(RELAY_ERROR_LABEL, &fields, key).map_err(object_refusal)?;
314 Ok(routed_frame(
315 name,
316 "relay_error",
317 &relay_error,
318 "source_route_partial",
319 spec.source_route_partial.clone(),
320 ))
321}
322
323pub fn verify_relay_error(
326 frame: &Value,
327 request: &VerifiedRequest,
328 profile: Profile,
329 expected_reporter: &[u8; 32],
330) -> Result<VerifiedRelayError, FrameError> {
331 let (frame_type, object) = received_frame(
332 frame,
333 "relay_error",
334 Rule::CarriedObject,
335 RELAY_ERROR_ROUTES,
336 &[ERROR, STREAM_ERROR],
337 )
338 .ok_or(FrameError::Malformed)?;
339 let verified = verify_object(RELAY_ERROR_LABEL, &object, profile).map_err(object_refusal)?;
340 let fields = read_fields(&verified.fields, &relay_error_table(&frame_type))
341 .ok_or(FrameError::Malformed)?;
342 if !has_fields(
343 &fields,
344 &[
345 "frame_type",
346 "request_id",
347 "request_hash",
348 "reported_by",
349 "code",
350 ],
351 ) {
352 return Err(FrameError::Malformed);
353 }
354 let relay_error = VerifiedRelayError {
355 frame_type: if frame_type == ERROR {
356 RelayErrorType::Error
357 } else {
358 RelayErrorType::StreamError
359 },
360 reported_by: fixed(&fields["reported_by"]),
361 code: text_of(&fields["code"]),
362 offending_hop: fields.get("offending_hop").map(fixed),
363 };
364 if relay_error.reported_by != node_id_of(&verified.key, profile) {
365 return Err(FrameError::KeyIdMismatch);
366 }
367 if !names_request(&fields, request) {
368 return Err(FrameError::RequestMismatch);
369 }
370 if &relay_error.reported_by != expected_reporter {
371 return Err(FrameError::NotTheConnection);
372 }
373 Ok(relay_error)
374}
375
376fn relay_error_table(frame_type: &str) -> Vec<(&'static str, Rule)> {
377 vec![
378 (
379 "frame_type",
380 Rule::TextIn(if frame_type == ERROR {
381 &[ERROR]
382 } else {
383 &[STREAM_ERROR]
384 }),
385 ),
386 ("alg", Rule::Any),
387 ("request_id", Rule::BytesOf(16)),
388 ("request_hash", Rule::BytesOf(48)),
389 ("reported_by", Rule::BytesOf(32)),
390 ("code", Rule::TextIn(RELAY_CODES)),
391 ("offending_hop", Rule::BytesOf(32)),
392 ]
393}
394
395pub fn claimed_reply_ids(frame: &Value) -> Result<([u8; 16], [u8; 48]), FrameError> {
401 let frame_type = frame.get("frame_type").map(text_of).unwrap_or_default();
402 let (object_name, routes, table) = match (frame.get("reply"), frame.get("relay_error")) {
403 (Some(_), _) if frame_type == RESULT || frame_type == ERROR => {
404 ("reply", REPLY_ROUTES, reply_table(&frame_type))
405 }
406 (_, Some(_)) if frame_type == ERROR || frame_type == STREAM_ERROR => (
407 "relay_error",
408 RELAY_ERROR_ROUTES,
409 relay_error_table(&frame_type),
410 ),
411 _ => return Err(FrameError::Malformed),
412 };
413 let types: &'static [&'static str] = match frame_type.as_str() {
414 RESULT => &[RESULT],
415 ERROR => &[ERROR],
416 _ => &[STREAM_ERROR],
417 };
418 let (_, object) = received_frame(frame, object_name, Rule::CarriedObject, routes, types)
419 .ok_or(FrameError::Malformed)?;
420 let parsed = Object::from_value(&object).map_err(|_| FrameError::Malformed)?;
421 let tbs = cbor::decode(&parsed.tbs).map_err(|_| FrameError::Malformed)?;
422 let fields = read_fields(&tbs, &table).ok_or(FrameError::Malformed)?;
423 if !has_fields(&fields, &["frame_type", "request_id", "request_hash"]) {
424 return Err(FrameError::Malformed);
425 }
426 Ok((fixed(&fields["request_id"]), fixed(&fields["request_hash"])))
427}
428
429fn routed_frame(
432 frame_type: &str,
433 object_name: &str,
434 object: &Object,
435 route_name: &str,
436 route: Option<Vec<u8>>,
437) -> Value {
438 let mut entries = vec![
439 entry("version", Value::Int(i128::from(PROTOCOL_VERSION))),
440 entry("frame_type", Value::text(frame_type)),
441 entry(object_name, object.to_value()),
442 ];
443 if let Some(route) = route {
444 entries.push(entry(route_name, Value::Bytes(route)));
445 }
446 Value::Map(entries)
447}