1use crate::error::{Error, Result};
28use crate::objects;
29use crate::tag::ApduTag;
30use broadcast_common::{Parse, Serialize};
31
32pub mod tag {
34 use crate::tag::ApduTag;
35 pub const DOWNLOAD_ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
37 pub const DOWNLOAD_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
39 pub const USER_AUTH_INITIATE: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
41 pub const USER_AUTH_RESULT: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x03);
43}
44
45pub const BINARY_ID_LEN: usize = 7;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize))]
52pub struct BinaryId {
53 pub specifier: u32,
55 pub model: u16,
57 pub version: u16,
59}
60
61impl BinaryId {
62 fn read(b: &[u8]) -> Self {
63 Self {
64 specifier: ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | b[2] as u32,
65 model: u16::from_be_bytes([b[3], b[4]]),
66 version: u16::from_be_bytes([b[5], b[6]]),
67 }
68 }
69 fn write(self, buf: &mut [u8]) {
70 buf[0] = (self.specifier >> 16) as u8;
71 buf[1] = (self.specifier >> 8) as u8;
72 buf[2] = self.specifier as u8;
73 buf[3..5].copy_from_slice(&self.model.to_be_bytes());
74 buf[5..7].copy_from_slice(&self.version.to_be_bytes());
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Default)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize))]
84pub struct DownloadEnquiry<'a> {
85 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
87 pub dsmcc_message: &'a [u8],
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Default)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize))]
94pub struct DownloadReply<'a> {
95 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
97 pub dsmcc_message: &'a [u8],
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Default)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize))]
103pub struct UserAuthInitiate<'a> {
104 pub binary_id: BinaryId,
106 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
108 pub data: &'a [u8],
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Default)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114pub struct UserAuthResult<'a> {
115 pub binary_id: BinaryId,
117 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
119 pub result: &'a [u8],
120}
121
122macro_rules! opaque_dsmcc_object {
123 ($ty:ident, $tag:expr, $what:literal) => {
124 impl<'a> Parse<'a> for $ty<'a> {
125 type Error = Error;
126 fn parse(bytes: &'a [u8]) -> Result<Self> {
127 let body = objects::parse_apdu_header(bytes, $tag, $what)?;
128 Ok(Self {
129 dsmcc_message: body,
130 })
131 }
132 }
133 impl Serialize for $ty<'_> {
134 type Error = Error;
135 fn serialized_len(&self) -> usize {
136 objects::apdu_len(self.dsmcc_message.len())
137 }
138 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
139 let body_len = self.dsmcc_message.len();
140 let pos = objects::write_apdu_header($tag, body_len, buf)?;
141 buf[pos..pos + body_len].copy_from_slice(self.dsmcc_message);
142 Ok(pos + body_len)
143 }
144 }
145 };
146}
147
148opaque_dsmcc_object!(DownloadEnquiry, tag::DOWNLOAD_ENQ, "download_enq");
149opaque_dsmcc_object!(DownloadReply, tag::DOWNLOAD_REPLY, "download_reply");
150
151macro_rules! user_auth_object {
152 ($ty:ident, $tag:expr, $what:literal, $field:ident) => {
153 impl<'a> Parse<'a> for $ty<'a> {
154 type Error = Error;
155 fn parse(bytes: &'a [u8]) -> Result<Self> {
156 let body = objects::parse_apdu_header(bytes, $tag, $what)?;
157 if body.len() < BINARY_ID_LEN {
158 return Err(Error::BufferTooShort {
159 need: BINARY_ID_LEN,
160 have: body.len(),
161 what: $what,
162 });
163 }
164 Ok(Self {
165 binary_id: BinaryId::read(body),
166 $field: &body[BINARY_ID_LEN..],
167 })
168 }
169 }
170 impl Serialize for $ty<'_> {
171 type Error = Error;
172 fn serialized_len(&self) -> usize {
173 objects::apdu_len(BINARY_ID_LEN + self.$field.len())
174 }
175 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
176 let body_len = BINARY_ID_LEN + self.$field.len();
177 let mut pos = objects::write_apdu_header($tag, body_len, buf)?;
178 self.binary_id.write(&mut buf[pos..]);
179 pos += BINARY_ID_LEN;
180 buf[pos..pos + self.$field.len()].copy_from_slice(self.$field);
181 Ok(pos + self.$field.len())
182 }
183 }
184 };
185}
186
187user_auth_object!(
188 UserAuthInitiate,
189 tag::USER_AUTH_INITIATE,
190 "user_authorization_initiate",
191 data
192);
193user_auth_object!(
194 UserAuthResult,
195 tag::USER_AUTH_RESULT,
196 "user_authorization_result",
197 result
198);
199
200pub const DSMCC_PROTOCOL_DISCRIMINATOR: u8 = 0x11;
210pub const DSMCC_TYPE_DOWNLOAD: u8 = 0x03;
212pub const MSG_ID_DOWNLOAD_INFO_REQUEST: u16 = 0x1001;
214pub const MSG_ID_DOWNLOAD_INFO_RESPONSE: u16 = 0x1002;
216pub const MSG_ID_DOWNLOAD_DATA_BLOCK: u16 = 0x1003;
218pub const MSG_ID_DOWNLOAD_DATA_REQUEST: u16 = 0x1004;
220pub const MSG_ID_DOWNLOAD_CANCEL: u16 = 0x1005;
222
223fn parse_dsmcc_header<'a>(
228 body: &'a [u8],
229 what: &'static str,
230) -> Result<(u32, u8, &'a [u8], &'a [u8])> {
231 const HDR: usize = 12;
234 if body.len() < HDR {
235 return Err(Error::BufferTooShort {
236 need: HDR,
237 have: body.len(),
238 what,
239 });
240 }
241 let transaction_id = u32::from_be_bytes([body[4], body[5], body[6], body[7]]);
242 let adaptation_length = body[9] as usize;
243 if body.len() < HDR + adaptation_length {
244 return Err(Error::BufferTooShort {
245 need: HDR + adaptation_length,
246 have: body.len(),
247 what,
248 });
249 }
250 let adaptation = &body[HDR..HDR + adaptation_length];
251 let rest = &body[HDR + adaptation_length..];
252 Ok((transaction_id, adaptation_length as u8, adaptation, rest))
253}
254
255fn write_dsmcc_header(
256 buf: &mut [u8],
257 message_id: u16,
258 transaction_id: u32,
259 adaptation: &[u8],
260 message_length: usize,
261) -> usize {
262 buf[0] = DSMCC_PROTOCOL_DISCRIMINATOR;
263 buf[1] = DSMCC_TYPE_DOWNLOAD;
264 buf[2..4].copy_from_slice(&message_id.to_be_bytes());
265 buf[4..8].copy_from_slice(&transaction_id.to_be_bytes());
266 buf[8] = 0xFF; buf[9] = adaptation.len() as u8;
268 buf[10..12].copy_from_slice(&(message_length as u16).to_be_bytes());
269 buf[12..12 + adaptation.len()].copy_from_slice(adaptation);
270 12 + adaptation.len()
271}
272
273#[derive(Debug, Clone, PartialEq, Eq, Default)]
277#[cfg_attr(feature = "serde", derive(serde::Serialize))]
278pub struct DownloadInfoRequest<'a> {
279 pub transaction_id: u32,
281 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
283 pub adaptation: &'a [u8],
284 pub buffer_size: u32,
286 pub maximum_block_size: u16,
288 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
291 pub compatibility_descriptor: &'a [u8],
292 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
294 pub private_data: &'a [u8],
295}
296
297impl<'a> Parse<'a> for DownloadInfoRequest<'a> {
298 type Error = Error;
299 fn parse(body: &'a [u8]) -> Result<Self> {
300 let what = "DownloadInfoRequest";
301 let (transaction_id, _adapt_len, adaptation, rest) = parse_dsmcc_header(body, what)?;
302 if rest.len() < 8 {
304 return Err(Error::BufferTooShort {
305 need: 8,
306 have: rest.len(),
307 what,
308 });
309 }
310 let buffer_size = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
311 let maximum_block_size = u16::from_be_bytes([rest[4], rest[5]]);
312 let compat_len = u16::from_be_bytes([rest[6], rest[7]]) as usize;
315 let compat_start = 6; let compat_block_end = compat_start + 2 + compat_len;
317 if rest.len() < compat_block_end + 2 {
318 return Err(Error::BufferTooShort {
319 need: compat_block_end + 2,
320 have: rest.len(),
321 what,
322 });
323 }
324 let compatibility_descriptor = &rest[compat_start..compat_block_end];
325 let priv_len =
326 u16::from_be_bytes([rest[compat_block_end], rest[compat_block_end + 1]]) as usize;
327 let priv_start = compat_block_end + 2;
328 let priv_end = priv_start + priv_len;
329 if rest.len() < priv_end {
330 return Err(Error::BufferTooShort {
331 need: priv_end,
332 have: rest.len(),
333 what,
334 });
335 }
336 Ok(Self {
337 transaction_id,
338 adaptation,
339 buffer_size,
340 maximum_block_size,
341 compatibility_descriptor,
342 private_data: &rest[priv_start..priv_end],
343 })
344 }
345}
346impl Serialize for DownloadInfoRequest<'_> {
347 type Error = Error;
348 fn serialized_len(&self) -> usize {
349 12 + self.adaptation.len()
350 + 6
351 + self.compatibility_descriptor.len()
352 + 2
353 + self.private_data.len()
354 }
355 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
356 let total = self.serialized_len();
357 if buf.len() < total {
358 return Err(Error::OutputBufferTooSmall {
359 need: total,
360 have: buf.len(),
361 });
362 }
363 let message_length = total - 12;
365 let mut pos = write_dsmcc_header(
366 buf,
367 MSG_ID_DOWNLOAD_INFO_REQUEST,
368 self.transaction_id,
369 self.adaptation,
370 message_length,
371 );
372 buf[pos..pos + 4].copy_from_slice(&self.buffer_size.to_be_bytes());
373 pos += 4;
374 buf[pos..pos + 2].copy_from_slice(&self.maximum_block_size.to_be_bytes());
375 pos += 2;
376 buf[pos..pos + self.compatibility_descriptor.len()]
377 .copy_from_slice(self.compatibility_descriptor);
378 pos += self.compatibility_descriptor.len();
379 buf[pos..pos + 2].copy_from_slice(&(self.private_data.len() as u16).to_be_bytes());
380 pos += 2;
381 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
382 Ok(pos + self.private_data.len())
383 }
384}
385
386#[derive(Debug, Clone, PartialEq, Eq, Default)]
390#[cfg_attr(feature = "serde", derive(serde::Serialize))]
391pub struct DownloadInfoResponse<'a> {
392 pub transaction_id: u32,
394 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
396 pub adaptation: &'a [u8],
397 pub download_id: u32,
399 pub block_size: u16,
401 pub window_size: u8,
403 pub ack_period: u8,
405 pub tc_download_window: u32,
407 pub tc_download_scenario: u32,
409 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
411 pub compatibility_descriptor: &'a [u8],
412 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
415 pub modules: &'a [u8],
416 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
418 pub private_data: &'a [u8],
419}
420
421impl<'a> Parse<'a> for DownloadInfoResponse<'a> {
422 type Error = Error;
423 fn parse(body: &'a [u8]) -> Result<Self> {
424 let what = "DownloadInfoResponse";
425 let (transaction_id, _adapt_len, adaptation, rest) = parse_dsmcc_header(body, what)?;
426 const FIXED: usize = 4 + 2 + 1 + 1 + 4 + 4 + 2;
429 if rest.len() < FIXED {
430 return Err(Error::BufferTooShort {
431 need: FIXED,
432 have: rest.len(),
433 what,
434 });
435 }
436 let download_id = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
437 let block_size = u16::from_be_bytes([rest[4], rest[5]]);
438 let window_size = rest[6];
439 let ack_period = rest[7];
440 let tc_download_window = u32::from_be_bytes([rest[8], rest[9], rest[10], rest[11]]);
441 let tc_download_scenario = u32::from_be_bytes([rest[12], rest[13], rest[14], rest[15]]);
442 let compat_len_off = 16;
443 let compat_len =
444 u16::from_be_bytes([rest[compat_len_off], rest[compat_len_off + 1]]) as usize;
445 let compat_block_end = compat_len_off + 2 + compat_len;
446 if rest.len() < compat_block_end + 2 {
448 return Err(Error::BufferTooShort {
449 need: compat_block_end + 2,
450 have: rest.len(),
451 what,
452 });
453 }
454 let compatibility_descriptor = &rest[compat_len_off..compat_block_end];
455 let number_of_modules =
457 u16::from_be_bytes([rest[compat_block_end], rest[compat_block_end + 1]]) as usize;
458 let mut mpos = compat_block_end + 2;
459 for _ in 0..number_of_modules {
460 if rest.len() < mpos + 8 {
462 return Err(Error::BufferTooShort {
463 need: mpos + 8,
464 have: rest.len(),
465 what,
466 });
467 }
468 let module_info_len = rest[mpos + 7] as usize;
469 mpos += 8 + module_info_len;
470 if rest.len() < mpos {
471 return Err(Error::BufferTooShort {
472 need: mpos,
473 have: rest.len(),
474 what,
475 });
476 }
477 }
478 let modules = &rest[compat_block_end..mpos];
479 if rest.len() < mpos + 2 {
480 return Err(Error::BufferTooShort {
481 need: mpos + 2,
482 have: rest.len(),
483 what,
484 });
485 }
486 let priv_len = u16::from_be_bytes([rest[mpos], rest[mpos + 1]]) as usize;
487 let priv_start = mpos + 2;
488 let priv_end = priv_start + priv_len;
489 if rest.len() < priv_end {
490 return Err(Error::BufferTooShort {
491 need: priv_end,
492 have: rest.len(),
493 what,
494 });
495 }
496 Ok(Self {
497 transaction_id,
498 adaptation,
499 download_id,
500 block_size,
501 window_size,
502 ack_period,
503 tc_download_window,
504 tc_download_scenario,
505 compatibility_descriptor,
506 modules,
507 private_data: &rest[priv_start..priv_end],
508 })
509 }
510}
511impl Serialize for DownloadInfoResponse<'_> {
512 type Error = Error;
513 fn serialized_len(&self) -> usize {
514 12 + self.adaptation.len()
515 + 16
516 + self.compatibility_descriptor.len()
517 + self.modules.len()
518 + 2
519 + self.private_data.len()
520 }
521 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
522 let total = self.serialized_len();
523 if buf.len() < total {
524 return Err(Error::OutputBufferTooSmall {
525 need: total,
526 have: buf.len(),
527 });
528 }
529 let message_length = total - 12;
530 let mut pos = write_dsmcc_header(
531 buf,
532 MSG_ID_DOWNLOAD_INFO_RESPONSE,
533 self.transaction_id,
534 self.adaptation,
535 message_length,
536 );
537 buf[pos..pos + 4].copy_from_slice(&self.download_id.to_be_bytes());
538 pos += 4;
539 buf[pos..pos + 2].copy_from_slice(&self.block_size.to_be_bytes());
540 pos += 2;
541 buf[pos] = self.window_size;
542 buf[pos + 1] = self.ack_period;
543 pos += 2;
544 buf[pos..pos + 4].copy_from_slice(&self.tc_download_window.to_be_bytes());
545 pos += 4;
546 buf[pos..pos + 4].copy_from_slice(&self.tc_download_scenario.to_be_bytes());
547 pos += 4;
548 buf[pos..pos + self.compatibility_descriptor.len()]
549 .copy_from_slice(self.compatibility_descriptor);
550 pos += self.compatibility_descriptor.len();
551 buf[pos..pos + self.modules.len()].copy_from_slice(self.modules);
552 pos += self.modules.len();
553 buf[pos..pos + 2].copy_from_slice(&(self.private_data.len() as u16).to_be_bytes());
554 pos += 2;
555 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
556 Ok(pos + self.private_data.len())
557 }
558}
559
560#[derive(Debug, Clone, PartialEq, Eq, Default)]
562#[cfg_attr(feature = "serde", derive(serde::Serialize))]
563pub struct DownloadCancel<'a> {
564 pub transaction_id: u32,
566 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
568 pub adaptation: &'a [u8],
569 pub download_id: u32,
571 pub module_id: u16,
573 pub block_number: u16,
575 pub download_cancel_reason: u8,
577 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
579 pub private_data: &'a [u8],
580}
581
582impl<'a> Parse<'a> for DownloadCancel<'a> {
583 type Error = Error;
584 fn parse(body: &'a [u8]) -> Result<Self> {
585 let what = "DownloadCancel";
586 let (transaction_id, _adapt_len, adaptation, rest) = parse_dsmcc_header(body, what)?;
587 const FIXED: usize = 4 + 2 + 2 + 1 + 2;
589 if rest.len() < FIXED {
590 return Err(Error::BufferTooShort {
591 need: FIXED,
592 have: rest.len(),
593 what,
594 });
595 }
596 let download_id = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
597 let module_id = u16::from_be_bytes([rest[4], rest[5]]);
598 let block_number = u16::from_be_bytes([rest[6], rest[7]]);
599 let download_cancel_reason = rest[8];
600 let priv_len = u16::from_be_bytes([rest[9], rest[10]]) as usize;
601 let priv_start = 11;
602 let priv_end = priv_start + priv_len;
603 if rest.len() < priv_end {
604 return Err(Error::BufferTooShort {
605 need: priv_end,
606 have: rest.len(),
607 what,
608 });
609 }
610 Ok(Self {
611 transaction_id,
612 adaptation,
613 download_id,
614 module_id,
615 block_number,
616 download_cancel_reason,
617 private_data: &rest[priv_start..priv_end],
618 })
619 }
620}
621impl Serialize for DownloadCancel<'_> {
622 type Error = Error;
623 fn serialized_len(&self) -> usize {
624 12 + self.adaptation.len() + 9 + 2 + self.private_data.len()
625 }
626 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
627 let total = self.serialized_len();
628 if buf.len() < total {
629 return Err(Error::OutputBufferTooSmall {
630 need: total,
631 have: buf.len(),
632 });
633 }
634 let message_length = total - 12;
635 let mut pos = write_dsmcc_header(
636 buf,
637 MSG_ID_DOWNLOAD_CANCEL,
638 self.transaction_id,
639 self.adaptation,
640 message_length,
641 );
642 buf[pos..pos + 4].copy_from_slice(&self.download_id.to_be_bytes());
643 pos += 4;
644 buf[pos..pos + 2].copy_from_slice(&self.module_id.to_be_bytes());
645 pos += 2;
646 buf[pos..pos + 2].copy_from_slice(&self.block_number.to_be_bytes());
647 pos += 2;
648 buf[pos] = self.download_cancel_reason;
649 pos += 1;
650 buf[pos..pos + 2].copy_from_slice(&(self.private_data.len() as u16).to_be_bytes());
651 pos += 2;
652 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
653 Ok(pos + self.private_data.len())
654 }
655}
656
657fn parse_dsmcc_data_header<'a>(
661 body: &'a [u8],
662 what: &'static str,
663) -> Result<(u32, &'a [u8], &'a [u8])> {
664 const HDR: usize = 12;
667 if body.len() < HDR {
668 return Err(Error::BufferTooShort {
669 need: HDR,
670 have: body.len(),
671 what,
672 });
673 }
674 let download_id = u32::from_be_bytes([body[4], body[5], body[6], body[7]]);
675 let adaptation_length = body[9] as usize;
676 if body.len() < HDR + adaptation_length {
677 return Err(Error::BufferTooShort {
678 need: HDR + adaptation_length,
679 have: body.len(),
680 what,
681 });
682 }
683 let adaptation = &body[HDR..HDR + adaptation_length];
684 Ok((download_id, adaptation, &body[HDR + adaptation_length..]))
685}
686
687fn write_dsmcc_data_header(
688 buf: &mut [u8],
689 message_id: u16,
690 download_id: u32,
691 adaptation: &[u8],
692 message_length: usize,
693) -> usize {
694 buf[0] = DSMCC_PROTOCOL_DISCRIMINATOR;
695 buf[1] = DSMCC_TYPE_DOWNLOAD;
696 buf[2..4].copy_from_slice(&message_id.to_be_bytes());
697 buf[4..8].copy_from_slice(&download_id.to_be_bytes());
698 buf[8] = 0xFF; buf[9] = adaptation.len() as u8;
700 buf[10..12].copy_from_slice(&(message_length as u16).to_be_bytes());
701 buf[12..12 + adaptation.len()].copy_from_slice(adaptation);
702 12 + adaptation.len()
703}
704
705#[derive(Debug, Clone, PartialEq, Eq, Default)]
707#[cfg_attr(feature = "serde", derive(serde::Serialize))]
708pub struct DownloadDataRequest<'a> {
709 pub download_id: u32,
711 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
713 pub adaptation: &'a [u8],
714 pub module_id: u16,
716 pub block_number: u16,
718 pub download_reason: u8,
720}
721
722impl<'a> Parse<'a> for DownloadDataRequest<'a> {
723 type Error = Error;
724 fn parse(body: &'a [u8]) -> Result<Self> {
725 let what = "DownloadDataRequest";
726 let (download_id, adaptation, rest) = parse_dsmcc_data_header(body, what)?;
727 const FIXED: usize = 5;
729 if rest.len() < FIXED {
730 return Err(Error::BufferTooShort {
731 need: FIXED,
732 have: rest.len(),
733 what,
734 });
735 }
736 Ok(Self {
737 download_id,
738 adaptation,
739 module_id: u16::from_be_bytes([rest[0], rest[1]]),
740 block_number: u16::from_be_bytes([rest[2], rest[3]]),
741 download_reason: rest[4],
742 })
743 }
744}
745impl Serialize for DownloadDataRequest<'_> {
746 type Error = Error;
747 fn serialized_len(&self) -> usize {
748 12 + self.adaptation.len() + 5
749 }
750 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
751 let total = self.serialized_len();
752 if buf.len() < total {
753 return Err(Error::OutputBufferTooSmall {
754 need: total,
755 have: buf.len(),
756 });
757 }
758 let message_length = total - 12;
759 let mut pos = write_dsmcc_data_header(
760 buf,
761 MSG_ID_DOWNLOAD_DATA_REQUEST,
762 self.download_id,
763 self.adaptation,
764 message_length,
765 );
766 buf[pos..pos + 2].copy_from_slice(&self.module_id.to_be_bytes());
767 pos += 2;
768 buf[pos..pos + 2].copy_from_slice(&self.block_number.to_be_bytes());
769 pos += 2;
770 buf[pos] = self.download_reason;
771 Ok(pos + 1)
772 }
773}
774
775#[derive(Debug, Clone, PartialEq, Eq, Default)]
778#[cfg_attr(feature = "serde", derive(serde::Serialize))]
779pub struct DownloadDataBlock<'a> {
780 pub download_id: u32,
782 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
784 pub adaptation: &'a [u8],
785 pub module_id: u16,
787 pub module_version: u8,
789 pub block_number: u16,
791 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
793 pub block_data: &'a [u8],
794}
795
796impl<'a> Parse<'a> for DownloadDataBlock<'a> {
797 type Error = Error;
798 fn parse(body: &'a [u8]) -> Result<Self> {
799 let what = "DownloadDataBlock";
800 let (download_id, adaptation, rest) = parse_dsmcc_data_header(body, what)?;
801 const FIXED: usize = 6;
803 if rest.len() < FIXED {
804 return Err(Error::BufferTooShort {
805 need: FIXED,
806 have: rest.len(),
807 what,
808 });
809 }
810 Ok(Self {
811 download_id,
812 adaptation,
813 module_id: u16::from_be_bytes([rest[0], rest[1]]),
814 module_version: rest[2],
815 block_number: u16::from_be_bytes([rest[4], rest[5]]),
817 block_data: &rest[FIXED..],
818 })
819 }
820}
821impl Serialize for DownloadDataBlock<'_> {
822 type Error = Error;
823 fn serialized_len(&self) -> usize {
824 12 + self.adaptation.len() + 6 + self.block_data.len()
825 }
826 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
827 let total = self.serialized_len();
828 if buf.len() < total {
829 return Err(Error::OutputBufferTooSmall {
830 need: total,
831 have: buf.len(),
832 });
833 }
834 let message_length = total - 12;
835 let mut pos = write_dsmcc_data_header(
836 buf,
837 MSG_ID_DOWNLOAD_DATA_BLOCK,
838 self.download_id,
839 self.adaptation,
840 message_length,
841 );
842 buf[pos..pos + 2].copy_from_slice(&self.module_id.to_be_bytes());
843 pos += 2;
844 buf[pos] = self.module_version;
845 buf[pos + 1] = 0xFF; pos += 2;
847 buf[pos..pos + 2].copy_from_slice(&self.block_number.to_be_bytes());
848 pos += 2;
849 buf[pos..pos + self.block_data.len()].copy_from_slice(self.block_data);
850 Ok(pos + self.block_data.len())
851 }
852}
853
854#[derive(Debug, Clone, PartialEq, Eq)]
856#[cfg_attr(feature = "serde", derive(serde::Serialize))]
857#[non_exhaustive]
858pub enum DownloadApdu<'a> {
859 DownloadEnquiry(DownloadEnquiry<'a>),
861 DownloadReply(DownloadReply<'a>),
863 UserAuthInitiate(UserAuthInitiate<'a>),
865 UserAuthResult(UserAuthResult<'a>),
867}
868
869impl<'a> DownloadApdu<'a> {
870 pub fn parse(body: &'a [u8]) -> Result<Self> {
872 if body.len() < 3 {
873 return Err(Error::BufferTooShort {
874 need: 3,
875 have: body.len(),
876 what: "download apdu_tag",
877 });
878 }
879 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
880 match t {
881 tag::DOWNLOAD_ENQ => Ok(Self::DownloadEnquiry(DownloadEnquiry::parse(body)?)),
882 tag::DOWNLOAD_REPLY => Ok(Self::DownloadReply(DownloadReply::parse(body)?)),
883 tag::USER_AUTH_INITIATE => Ok(Self::UserAuthInitiate(UserAuthInitiate::parse(body)?)),
884 tag::USER_AUTH_RESULT => Ok(Self::UserAuthResult(UserAuthResult::parse(body)?)),
885 _ => Err(Error::UnexpectedApduTag {
886 got: t.as_u24(),
887 expected: tag::DOWNLOAD_ENQ.as_u24(),
888 what: "download",
889 }),
890 }
891 }
892}
893
894impl Serialize for DownloadApdu<'_> {
895 type Error = Error;
896 fn serialized_len(&self) -> usize {
897 match self {
898 Self::DownloadEnquiry(o) => o.serialized_len(),
899 Self::DownloadReply(o) => o.serialized_len(),
900 Self::UserAuthInitiate(o) => o.serialized_len(),
901 Self::UserAuthResult(o) => o.serialized_len(),
902 }
903 }
904 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
905 match self {
906 Self::DownloadEnquiry(o) => o.serialize_into(buf),
907 Self::DownloadReply(o) => o.serialize_into(buf),
908 Self::UserAuthInitiate(o) => o.serialize_into(buf),
909 Self::UserAuthResult(o) => o.serialize_into(buf),
910 }
911 }
912}
913
914#[cfg(test)]
915mod tests {
916 use super::*;
917
918 #[test]
919 fn download_enquiry_round_trips_and_bites() {
920 let enq = DownloadEnquiry {
921 dsmcc_message: &[0x11, 0x03, 0x10, 0x01],
922 };
923 let bytes = enq.to_bytes();
924 assert_eq!(bytes, [0x9F, 0x80, 0x00, 0x04, 0x11, 0x03, 0x10, 0x01]);
925 assert_eq!(DownloadEnquiry::parse(&bytes).unwrap(), enq);
926 let other = DownloadEnquiry {
927 dsmcc_message: &[0x11, 0x03, 0x10, 0x02],
928 };
929 assert_ne!(bytes, other.to_bytes());
930 }
931
932 #[test]
933 fn download_reply_round_trips() {
934 let rep = DownloadReply {
935 dsmcc_message: &[0xAA],
936 };
937 let bytes = rep.to_bytes();
938 assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x01, 0xAA]);
939 assert_eq!(DownloadReply::parse(&bytes).unwrap(), rep);
940 }
941
942 #[test]
943 fn user_auth_initiate_round_trips_and_bites() {
944 let uai = UserAuthInitiate {
945 binary_id: BinaryId {
946 specifier: 0x00_1B_67,
947 model: 0x1234,
948 version: 0x0005,
949 },
950 data: &[0xCA, 0xFE],
951 };
952 let bytes = uai.to_bytes();
953 assert_eq!(
955 bytes,
956 [
957 0x9F, 0x80, 0x02, 0x09, 0x00, 0x1B, 0x67, 0x12, 0x34, 0x00, 0x05, 0xCA, 0xFE
958 ]
959 );
960 assert_eq!(UserAuthInitiate::parse(&bytes).unwrap(), uai);
961 let mut other = uai.clone();
962 other.binary_id.version = 0x0006;
963 assert_ne!(bytes, other.to_bytes());
964 }
965
966 #[test]
967 fn user_auth_result_round_trips() {
968 let uar = UserAuthResult {
969 binary_id: BinaryId {
970 specifier: 0xAABBCC & 0x00FF_FFFF,
971 model: 1,
972 version: 2,
973 },
974 result: &[0x01],
975 };
976 let bytes = uar.to_bytes();
977 assert_eq!(
978 bytes,
979 [
980 0x9F, 0x80, 0x03, 0x08, 0xAA, 0xBB, 0xCC, 0x00, 0x01, 0x00, 0x02, 0x01
981 ]
982 );
983 assert_eq!(UserAuthResult::parse(&bytes).unwrap(), uar);
984 }
985
986 #[test]
987 fn download_info_request_round_trips_and_bites() {
988 let compat = [0x00, 0x04, 0x00, 0x00, 0xAA, 0xBB];
993 let req = DownloadInfoRequest {
994 transaction_id: 0x0000_0001,
995 adaptation: &[],
996 buffer_size: 0x0001_0000,
997 maximum_block_size: 0x0200,
998 compatibility_descriptor: &compat,
999 private_data: &[],
1000 };
1001 let bytes = req.to_bytes();
1002 assert_eq!(DownloadInfoRequest::parse(&bytes).unwrap(), req);
1003 assert_eq!(bytes[0], 0x11);
1005 assert_eq!(bytes[1], 0x03);
1006 assert_eq!(&bytes[2..4], &[0x10, 0x01]);
1007 let mut other = req.clone();
1008 other.buffer_size = 0x0002_0000;
1009 assert_ne!(bytes, other.to_bytes());
1010 }
1011
1012 #[test]
1013 fn download_info_request_with_adaptation() {
1014 let compat = [0x00, 0x02, 0x00, 0x00];
1015 let req = DownloadInfoRequest {
1016 transaction_id: 0x12,
1017 adaptation: &[0x01, 0x02, 0x03],
1018 buffer_size: 1,
1019 maximum_block_size: 2,
1020 compatibility_descriptor: &compat,
1021 private_data: &[],
1022 };
1023 let bytes = req.to_bytes();
1024 assert_eq!(bytes[9], 0x03); assert_eq!(DownloadInfoRequest::parse(&bytes).unwrap(), req);
1026 }
1027
1028 #[test]
1029 fn download_info_response_multi_module_round_trips_and_bites() {
1030 let compat = [0x00, 0x02, 0x00, 0x00];
1031 let modules = [
1035 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x01, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, ];
1039 let resp = DownloadInfoResponse {
1040 transaction_id: 1,
1041 adaptation: &[],
1042 download_id: 0xDEAD_BEEF,
1043 block_size: 0x0100,
1044 window_size: 4,
1045 ack_period: 2,
1046 tc_download_window: 1000,
1047 tc_download_scenario: 2000,
1048 compatibility_descriptor: &compat,
1049 modules: &modules,
1050 private_data: &[],
1051 };
1052 let bytes = resp.to_bytes();
1053 assert_eq!(DownloadInfoResponse::parse(&bytes).unwrap(), resp);
1054 let mut other = resp.clone();
1055 other.window_size = 5;
1056 assert_ne!(bytes, other.to_bytes());
1057 }
1058
1059 #[test]
1060 fn download_cancel_round_trips() {
1061 let cancel = DownloadCancel {
1062 transaction_id: 0x10,
1063 adaptation: &[],
1064 download_id: 0x01,
1065 module_id: 0x02,
1066 block_number: 0x03,
1067 download_cancel_reason: 0x05,
1068 private_data: &[],
1069 };
1070 let bytes = cancel.to_bytes();
1071 assert_eq!(DownloadCancel::parse(&bytes).unwrap(), cancel);
1072 assert_eq!(&bytes[2..4], &[0x10, 0x05]); }
1074
1075 #[test]
1076 fn download_data_request_round_trips_and_bites() {
1077 let req = DownloadDataRequest {
1078 download_id: 0xDEAD_BEEF,
1079 adaptation: &[],
1080 module_id: 0x0001,
1081 block_number: 0x0002,
1082 download_reason: 0x00,
1083 };
1084 let bytes = req.to_bytes();
1085 assert_eq!(DownloadDataRequest::parse(&bytes).unwrap(), req);
1086 assert_eq!(&bytes[2..4], &[0x10, 0x04]);
1089 assert_eq!(&bytes[4..8], &0xDEAD_BEEFu32.to_be_bytes());
1090 let mut other = req.clone();
1091 other.block_number = 0x0003;
1092 assert_ne!(bytes, other.to_bytes());
1093 }
1094
1095 #[test]
1096 fn download_data_block_round_trips_and_bites() {
1097 let block = DownloadDataBlock {
1098 download_id: 0x0000_0001,
1099 adaptation: &[],
1100 module_id: 0x0001,
1101 module_version: 0x02,
1102 block_number: 0x0003,
1103 block_data: &[0xFE, 0xED, 0xFA, 0xCE],
1104 };
1105 let bytes = block.to_bytes();
1106 assert_eq!(DownloadDataBlock::parse(&bytes).unwrap(), block);
1107 assert_eq!(&bytes[2..4], &[0x10, 0x03]); let mut other = block.clone();
1109 other.block_data = &[0xFE, 0xED, 0xFA, 0xCF];
1110 assert_ne!(bytes, other.to_bytes());
1111 }
1112
1113 #[test]
1114 fn enquiry_round_trips_a_real_dsmcc_message() {
1115 let inner = DownloadDataRequest {
1117 download_id: 0x1234_5678,
1118 adaptation: &[],
1119 module_id: 1,
1120 block_number: 1,
1121 download_reason: 0,
1122 };
1123 let inner_bytes = inner.to_bytes();
1124 let enq = DownloadEnquiry {
1125 dsmcc_message: &inner_bytes,
1126 };
1127 let outer = enq.to_bytes();
1128 let parsed = DownloadEnquiry::parse(&outer).unwrap();
1129 assert_eq!(parsed, enq);
1130 assert_eq!(
1132 DownloadDataRequest::parse(parsed.dsmcc_message).unwrap(),
1133 inner
1134 );
1135 }
1136
1137 #[test]
1138 fn dispatch_routes_each_tag() {
1139 let enq = DownloadEnquiry {
1140 dsmcc_message: &[0x11],
1141 }
1142 .to_bytes();
1143 assert!(matches!(
1144 DownloadApdu::parse(&enq).unwrap(),
1145 DownloadApdu::DownloadEnquiry(_)
1146 ));
1147 let uar = UserAuthResult {
1148 binary_id: BinaryId::default(),
1149 result: &[0x01],
1150 }
1151 .to_bytes();
1152 let parsed = DownloadApdu::parse(&uar).unwrap();
1153 assert!(matches!(parsed, DownloadApdu::UserAuthResult(_)));
1154 assert_eq!(parsed.to_bytes(), uar);
1155 }
1156}