1use super::ber::{BerReader, BerWriter, Class, tags};
9use super::isp1::Credentials;
10use super::types::{ServiceType, SleError};
11
12const MAX_ID_LEN: usize = 64;
14
15const MAX_CLTU_DATA: usize = 2048;
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20#[repr(u8)]
21pub enum CltuOp {
22 Bind = 0,
24 BindReturn = 1,
26 Unbind = 2,
28 UnbindReturn = 3,
30 Start = 4,
32 StartReturn = 5,
34 Stop = 6,
36 StopReturn = 7,
38 TransferData = 8,
40 TransferDataReturn = 9,
42}
43
44#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46#[repr(u8)]
47pub enum CltuStatus {
48 Radiated = 0,
50 Expired = 1,
52 Interrupted = 2,
54 ProductionNotStarted = 3,
56}
57
58impl CltuStatus {
59 pub fn from_i64(v: i64) -> Result<Self, SleError> {
61 match v {
62 0 => Ok(Self::Radiated),
63 1 => Ok(Self::Expired),
64 2 => Ok(Self::Interrupted),
65 3 => Ok(Self::ProductionNotStarted),
66 _ => Err(SleError::InvalidEnumValue),
67 }
68 }
69}
70
71#[derive(Clone, Debug)]
73pub struct CltuBindInvocation {
74 pub initiator_id: [u8; MAX_ID_LEN],
76 pub initiator_id_len: usize,
78 pub responder_id: [u8; MAX_ID_LEN],
80 pub responder_id_len: usize,
82 pub service_type: ServiceType,
84 pub version: u16,
86 pub credentials: Option<Credentials>,
88}
89
90impl CltuBindInvocation {
91 pub fn new(
93 initiator_id: &[u8],
94 responder_id: &[u8],
95 version: u16,
96 credentials: Option<Credentials>,
97 ) -> Result<Self, SleError> {
98 if initiator_id.len() > MAX_ID_LEN
99 || responder_id.len() > MAX_ID_LEN
100 {
101 return Err(SleError::TooLong);
102 }
103 let mut init = [0u8; MAX_ID_LEN];
104 init[..initiator_id.len()]
105 .copy_from_slice(initiator_id);
106 let mut resp = [0u8; MAX_ID_LEN];
107 resp[..responder_id.len()]
108 .copy_from_slice(responder_id);
109 Ok(Self {
110 initiator_id: init,
111 initiator_id_len: initiator_id.len(),
112 responder_id: resp,
113 responder_id_len: responder_id.len(),
114 service_type: ServiceType::FCltu,
115 version,
116 credentials,
117 })
118 }
119
120 pub fn initiator_id(&self) -> &[u8] {
122 &self.initiator_id[..self.initiator_id_len]
123 }
124
125 pub fn responder_id(&self) -> &[u8] {
127 &self.responder_id[..self.responder_id_len]
128 }
129
130 pub fn encode(
132 &self,
133 buf: &mut [u8],
134 ) -> Result<usize, SleError> {
135 let mut w = BerWriter::new(buf);
136 let outer = w.begin_context(
137 CltuOp::Bind as u8,
138 true,
139 )?;
140 let seq = w.begin_sequence()?;
141
142 match &self.credentials {
143 None => w.write_null()?,
144 Some(cred) => {
145 let cred_seq = w.begin_sequence()?;
146 w.write_octet_string(&cred.time)?;
147 w.write_integer(cred.random as i64)?;
148 w.write_octet_string(&cred.hash)?;
149 w.end_sequence(cred_seq)?;
150 }
151 }
152
153 w.write_octet_string(self.initiator_id())?;
154 w.write_octet_string(self.responder_id())?;
155 w.write_enum(self.service_type as i64)?;
156 w.write_integer(self.version as i64)?;
157
158 w.end_sequence(seq)?;
159 w.end_sequence(outer)?;
160 Ok(w.len())
161 }
162
163 pub fn decode(buf: &[u8]) -> Result<Self, SleError> {
165 let mut r = BerReader::new(buf);
166
167 let (tag, _len) = r.read_context_tag()?;
168 if tag != CltuOp::Bind as u8 {
169 return Err(SleError::UnexpectedTag);
170 }
171
172 let _seq_len = r.read_sequence()?;
173
174 let (peek_tag, peek_class, _) = r.peek_tag()?;
175 let credentials = if peek_tag == tags::NULL
176 && peek_class == Class::Universal
177 {
178 r.read_null()?;
179 None
180 } else {
181 let _cred_len = r.read_sequence()?;
182 let time_bytes = r.read_octet_string()?;
183 let random = r.read_integer()? as u32;
184 let hash_bytes = r.read_octet_string()?;
185 let mut time = [0u8; 8];
186 time.copy_from_slice(
187 time_bytes.get(..8).ok_or(SleError::Truncated)?,
188 );
189 let mut hash = [0u8; 20];
190 hash.copy_from_slice(
191 hash_bytes
192 .get(..20)
193 .ok_or(SleError::Truncated)?,
194 );
195 Some(Credentials { time, random, hash })
196 };
197
198 let initiator = r.read_octet_string()?;
199 let responder = r.read_octet_string()?;
200 let _service_type =
201 ServiceType::from_u8(r.read_enum()? as u8)?;
202 let version = r.read_integer()? as u16;
203
204 Self::new(initiator, responder, version, credentials)
205 }
206}
207
208#[derive(Copy, Clone, Debug, PartialEq, Eq)]
213pub struct CltuStartInvocation {
214 pub credentials: Option<Credentials>,
216 pub invoke_id: u16,
218 pub first_cltu_id: u32,
220}
221
222impl CltuStartInvocation {
223 pub fn encode(
225 &self,
226 buf: &mut [u8],
227 ) -> Result<usize, SleError> {
228 let mut w = BerWriter::new(buf);
229 let outer = w.begin_context(
230 CltuOp::Start as u8,
231 true,
232 )?;
233 let seq = w.begin_sequence()?;
234
235 match &self.credentials {
236 None => w.write_null()?,
237 Some(cred) => {
238 let cred_seq = w.begin_sequence()?;
239 w.write_octet_string(&cred.time)?;
240 w.write_integer(cred.random as i64)?;
241 w.write_octet_string(&cred.hash)?;
242 w.end_sequence(cred_seq)?;
243 }
244 }
245
246 w.write_integer(self.invoke_id as i64)?;
247 w.write_integer(self.first_cltu_id as i64)?;
248
249 w.end_sequence(seq)?;
250 w.end_sequence(outer)?;
251 Ok(w.len())
252 }
253
254 pub fn decode(buf: &[u8]) -> Result<Self, SleError> {
256 let mut r = BerReader::new(buf);
257
258 let (tag, _len) = r.read_context_tag()?;
259 if tag != CltuOp::Start as u8 {
260 return Err(SleError::UnexpectedTag);
261 }
262
263 let _seq_len = r.read_sequence()?;
264
265 let (peek_tag, peek_class, _) = r.peek_tag()?;
266 let credentials = if peek_tag == tags::NULL
267 && peek_class == Class::Universal
268 {
269 r.read_null()?;
270 None
271 } else {
272 let _cred_len = r.read_sequence()?;
273 let time_bytes = r.read_octet_string()?;
274 let random = r.read_integer()? as u32;
275 let hash_bytes = r.read_octet_string()?;
276 let mut time = [0u8; 8];
277 time.copy_from_slice(
278 time_bytes.get(..8).ok_or(SleError::Truncated)?,
279 );
280 let mut hash = [0u8; 20];
281 hash.copy_from_slice(
282 hash_bytes
283 .get(..20)
284 .ok_or(SleError::Truncated)?,
285 );
286 Some(Credentials { time, random, hash })
287 };
288
289 let invoke_id = r.read_integer()? as u16;
290 let first_cltu_id = r.read_integer()? as u32;
291
292 Ok(Self {
293 credentials,
294 invoke_id,
295 first_cltu_id,
296 })
297 }
298}
299
300#[derive(Clone, Debug)]
302pub struct CltuTransferDataInvocation {
303 pub cltu_id: u32,
305 data_buf: [u8; MAX_CLTU_DATA],
307 data_len: usize,
309 pub credentials: Option<Credentials>,
311}
312
313impl CltuTransferDataInvocation {
314 pub fn new(
316 cltu_id: u32,
317 data: &[u8],
318 credentials: Option<Credentials>,
319 ) -> Result<Self, SleError> {
320 if data.len() > MAX_CLTU_DATA {
321 return Err(SleError::TooLong);
322 }
323 let mut data_buf = [0u8; MAX_CLTU_DATA];
324 data_buf[..data.len()].copy_from_slice(data);
325 Ok(Self {
326 cltu_id,
327 data_buf,
328 data_len: data.len(),
329 credentials,
330 })
331 }
332
333 pub fn data(&self) -> &[u8] {
335 &self.data_buf[..self.data_len]
336 }
337
338 pub fn encode(
340 &self,
341 buf: &mut [u8],
342 ) -> Result<usize, SleError> {
343 let mut w = BerWriter::new(buf);
344 let outer = w.begin_context(
345 CltuOp::TransferData as u8,
346 true,
347 )?;
348 let seq = w.begin_sequence()?;
349
350 match &self.credentials {
351 None => w.write_null()?,
352 Some(cred) => {
353 let cred_seq = w.begin_sequence()?;
354 w.write_octet_string(&cred.time)?;
355 w.write_integer(cred.random as i64)?;
356 w.write_octet_string(&cred.hash)?;
357 w.end_sequence(cred_seq)?;
358 }
359 }
360
361 w.write_integer(self.cltu_id as i64)?;
362 w.write_octet_string(self.data())?;
363
364 w.end_sequence(seq)?;
365 w.end_sequence(outer)?;
366 Ok(w.len())
367 }
368
369 pub fn decode(buf: &[u8]) -> Result<Self, SleError> {
371 let mut r = BerReader::new(buf);
372
373 let (tag, _len) = r.read_context_tag()?;
374 if tag != CltuOp::TransferData as u8 {
375 return Err(SleError::UnexpectedTag);
376 }
377
378 let _seq_len = r.read_sequence()?;
379
380 let (peek_tag, peek_class, _) = r.peek_tag()?;
381 let credentials = if peek_tag == tags::NULL
382 && peek_class == Class::Universal
383 {
384 r.read_null()?;
385 None
386 } else {
387 let _cred_len = r.read_sequence()?;
388 let time_bytes = r.read_octet_string()?;
389 let random = r.read_integer()? as u32;
390 let hash_bytes = r.read_octet_string()?;
391 let mut time = [0u8; 8];
392 time.copy_from_slice(
393 time_bytes.get(..8).ok_or(SleError::Truncated)?,
394 );
395 let mut hash = [0u8; 20];
396 hash.copy_from_slice(
397 hash_bytes
398 .get(..20)
399 .ok_or(SleError::Truncated)?,
400 );
401 Some(Credentials { time, random, hash })
402 };
403
404 let cltu_id = r.read_integer()? as u32;
405 let data = r.read_octet_string()?;
406
407 Self::new(cltu_id, data, credentials)
408 }
409}
410
411#[derive(Copy, Clone, Debug, PartialEq, Eq)]
413pub struct CltuTransferDataReturn {
414 pub cltu_id: u32,
416 pub status: CltuStatus,
418 pub credentials: Option<Credentials>,
420}
421
422impl CltuTransferDataReturn {
423 pub fn encode(
425 &self,
426 buf: &mut [u8],
427 ) -> Result<usize, SleError> {
428 let mut w = BerWriter::new(buf);
429 let outer = w.begin_context(
430 CltuOp::TransferDataReturn as u8,
431 true,
432 )?;
433 let seq = w.begin_sequence()?;
434
435 match &self.credentials {
436 None => w.write_null()?,
437 Some(cred) => {
438 let cred_seq = w.begin_sequence()?;
439 w.write_octet_string(&cred.time)?;
440 w.write_integer(cred.random as i64)?;
441 w.write_octet_string(&cred.hash)?;
442 w.end_sequence(cred_seq)?;
443 }
444 }
445
446 w.write_integer(self.cltu_id as i64)?;
447 w.write_enum(self.status as i64)?;
448
449 w.end_sequence(seq)?;
450 w.end_sequence(outer)?;
451 Ok(w.len())
452 }
453
454 pub fn decode(buf: &[u8]) -> Result<Self, SleError> {
456 let mut r = BerReader::new(buf);
457
458 let (tag, _len) = r.read_context_tag()?;
459 if tag != CltuOp::TransferDataReturn as u8 {
460 return Err(SleError::UnexpectedTag);
461 }
462
463 let _seq_len = r.read_sequence()?;
464
465 let (peek_tag, peek_class, _) = r.peek_tag()?;
466 let credentials = if peek_tag == tags::NULL
467 && peek_class == Class::Universal
468 {
469 r.read_null()?;
470 None
471 } else {
472 let _cred_len = r.read_sequence()?;
473 let time_bytes = r.read_octet_string()?;
474 let random = r.read_integer()? as u32;
475 let hash_bytes = r.read_octet_string()?;
476 let mut time = [0u8; 8];
477 time.copy_from_slice(
478 time_bytes.get(..8).ok_or(SleError::Truncated)?,
479 );
480 let mut hash = [0u8; 20];
481 hash.copy_from_slice(
482 hash_bytes
483 .get(..20)
484 .ok_or(SleError::Truncated)?,
485 );
486 Some(Credentials { time, random, hash })
487 };
488
489 let cltu_id = r.read_integer()? as u32;
490 let status =
491 CltuStatus::from_i64(r.read_enum()?)?;
492
493 Ok(Self {
494 cltu_id,
495 status,
496 credentials,
497 })
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504
505 #[test]
506 fn cltu_bind_roundtrip() {
507 let bind = CltuBindInvocation::new(
508 b"operator",
509 b"antenna-1",
510 3,
511 None,
512 )
513 .unwrap();
514
515 let mut buf = [0u8; 256];
516 let n = bind.encode(&mut buf).unwrap();
517
518 let decoded =
519 CltuBindInvocation::decode(&buf[..n]).unwrap();
520 assert_eq!(decoded.initiator_id(), b"operator");
521 assert_eq!(decoded.responder_id(), b"antenna-1");
522 assert_eq!(decoded.service_type, ServiceType::FCltu);
523 assert_eq!(decoded.version, 3);
524 assert!(decoded.credentials.is_none());
525 }
526
527 #[test]
528 fn cltu_start_roundtrip() {
529 let start = CltuStartInvocation {
530 credentials: None,
531 invoke_id: 1,
532 first_cltu_id: 100,
533 };
534
535 let mut buf = [0u8; 128];
536 let n = start.encode(&mut buf).unwrap();
537
538 let decoded =
539 CltuStartInvocation::decode(&buf[..n]).unwrap();
540 assert_eq!(decoded.invoke_id, 1);
541 assert_eq!(decoded.first_cltu_id, 100);
542 assert!(decoded.credentials.is_none());
543 }
544
545 #[test]
546 fn cltu_transfer_data_roundtrip() {
547 let cltu_data = [0x01, 0x02, 0x03, 0x04, 0x05];
548 let td = CltuTransferDataInvocation::new(
549 42,
550 &cltu_data,
551 None,
552 )
553 .unwrap();
554
555 let mut buf = [0u8; 128];
556 let n = td.encode(&mut buf).unwrap();
557
558 let decoded =
559 CltuTransferDataInvocation::decode(&buf[..n])
560 .unwrap();
561 assert_eq!(decoded.cltu_id, 42);
562 assert_eq!(decoded.data(), &cltu_data);
563 assert!(decoded.credentials.is_none());
564 }
565
566 #[test]
567 fn cltu_transfer_data_return_roundtrip() {
568 let ret = CltuTransferDataReturn {
569 cltu_id: 42,
570 status: CltuStatus::Radiated,
571 credentials: None,
572 };
573
574 let mut buf = [0u8; 64];
575 let n = ret.encode(&mut buf).unwrap();
576
577 let decoded =
578 CltuTransferDataReturn::decode(&buf[..n]).unwrap();
579 assert_eq!(decoded.cltu_id, 42);
580 assert_eq!(decoded.status, CltuStatus::Radiated);
581 assert!(decoded.credentials.is_none());
582 }
583}