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