1use crate::error::{Error, Result};
13use crate::length;
14use crate::tag::{self, ApduTag};
15use crate::traits::ApduDef;
16use alloc::vec::Vec;
17use broadcast_common::{Parse, Serialize};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25pub struct Text<'a> {
26 pub more: bool,
28 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
30 pub text_chars: &'a [u8],
31}
32
33impl<'a> Text<'a> {
34 #[must_use]
36 pub fn tag(&self) -> ApduTag {
37 if self.more {
38 tag::TEXT_MORE
39 } else {
40 tag::TEXT_LAST
41 }
42 }
43
44 pub(crate) fn parse_component(bytes: &'a [u8]) -> Result<(Self, usize)> {
47 if bytes.len() < 3 {
48 return Err(Error::BufferTooShort {
49 need: 3,
50 have: bytes.len(),
51 what: "text component tag",
52 });
53 }
54 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
55 let more = match t {
56 tag::TEXT_LAST => false,
57 tag::TEXT_MORE => true,
58 _ => {
59 return Err(Error::UnexpectedApduTag {
60 got: t.as_u24(),
61 expected: tag::TEXT_LAST.as_u24(),
62 what: "text component",
63 });
64 }
65 };
66 let (len_value, len_hdr) = length::decode(&bytes[3..])?;
67 let body_start = 3 + len_hdr;
68 let body_end = body_start + len_value;
69 if bytes.len() < body_end {
70 return Err(Error::LengthMismatch {
71 what: "text component",
72 declared: len_value,
73 actual: bytes.len().saturating_sub(body_start),
74 });
75 }
76 Ok((
77 Self {
78 more,
79 text_chars: &bytes[body_start..body_end],
80 },
81 body_end,
82 ))
83 }
84
85 pub(crate) fn component_len(&self) -> usize {
87 super::apdu_len(self.text_chars.len())
88 }
89
90 pub(crate) fn serialize_component(&self, buf: &mut [u8]) -> Result<usize> {
92 let mut pos = super::write_apdu_header(self.tag(), self.text_chars.len(), buf)?;
93 buf[pos..pos + self.text_chars.len()].copy_from_slice(self.text_chars);
94 pos += self.text_chars.len();
95 Ok(pos)
96 }
97}
98
99impl<'a> Parse<'a> for Text<'a> {
100 type Error = Error;
101 fn parse(bytes: &'a [u8]) -> Result<Self> {
102 let (t, consumed) = Self::parse_component(bytes)?;
103 if consumed != bytes.len() {
105 return Err(Error::LengthMismatch {
106 what: "text",
107 declared: consumed,
108 actual: bytes.len(),
109 });
110 }
111 Ok(t)
112 }
113}
114
115impl Serialize for Text<'_> {
116 type Error = Error;
117 fn serialized_len(&self) -> usize {
118 self.component_len()
119 }
120 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
121 self.serialize_component(buf)
122 }
123}
124
125impl<'a> ApduDef<'a> for Text<'a> {
126 const TAG: ApduTag = tag::TEXT_LAST;
127 const NAME: &'static str = "TEXT";
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize))]
133pub struct Enq<'a> {
134 pub blind_answer: bool,
136 pub answer_text_length: u8,
138 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
140 pub text_chars: &'a [u8],
141}
142
143const ENQ_PREFIX: usize = 2;
145
146impl<'a> Parse<'a> for Enq<'a> {
147 type Error = Error;
148 fn parse(bytes: &'a [u8]) -> Result<Self> {
149 let body = super::parse_apdu_header(bytes, tag::ENQ, "enq")?;
150 if body.len() < ENQ_PREFIX {
151 return Err(Error::BufferTooShort {
152 need: ENQ_PREFIX,
153 have: body.len(),
154 what: "enq",
155 });
156 }
157 Ok(Self {
158 blind_answer: (body[0] & 0x01) != 0,
159 answer_text_length: body[1],
160 text_chars: &body[ENQ_PREFIX..],
161 })
162 }
163}
164
165impl Serialize for Enq<'_> {
166 type Error = Error;
167 fn serialized_len(&self) -> usize {
168 super::apdu_len(ENQ_PREFIX + self.text_chars.len())
169 }
170 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
171 let body_len = ENQ_PREFIX + self.text_chars.len();
172 let mut pos = super::write_apdu_header(tag::ENQ, body_len, buf)?;
173 buf[pos] = 0xFE | u8::from(self.blind_answer);
175 buf[pos + 1] = self.answer_text_length;
176 pos += ENQ_PREFIX;
177 buf[pos..pos + self.text_chars.len()].copy_from_slice(self.text_chars);
178 pos += self.text_chars.len();
179 Ok(pos)
180 }
181}
182
183impl<'a> ApduDef<'a> for Enq<'a> {
184 const TAG: ApduTag = tag::ENQ;
185 const NAME: &'static str = "ENQ";
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190#[cfg_attr(feature = "serde", derive(serde::Serialize))]
191#[non_exhaustive]
192pub enum AnswId {
193 Cancel,
195 Answer,
197 Reserved(u8),
199}
200
201impl AnswId {
202 #[must_use]
204 pub fn from_u8(v: u8) -> Self {
205 match v {
206 0x00 => Self::Cancel,
207 0x01 => Self::Answer,
208 other => Self::Reserved(other),
209 }
210 }
211 #[must_use]
213 pub const fn to_u8(self) -> u8 {
214 match self {
215 Self::Cancel => 0x00,
216 Self::Answer => 0x01,
217 Self::Reserved(v) => v,
218 }
219 }
220 #[must_use]
222 pub fn name(&self) -> &'static str {
223 match self {
224 Self::Cancel => "cancel",
225 Self::Answer => "answer",
226 Self::Reserved(_) => "reserved",
227 }
228 }
229}
230broadcast_common::impl_spec_display!(AnswId, Reserved);
231
232#[derive(Debug, Clone, PartialEq, Eq)]
234#[cfg_attr(feature = "serde", derive(serde::Serialize))]
235pub struct Answ<'a> {
236 pub answ_id: AnswId,
238 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
240 pub text_chars: &'a [u8],
241}
242
243impl<'a> Parse<'a> for Answ<'a> {
244 type Error = Error;
245 fn parse(bytes: &'a [u8]) -> Result<Self> {
246 let body = super::parse_apdu_header(bytes, tag::ANSW, "answ")?;
247 let id_byte = *body.first().ok_or(Error::BufferTooShort {
248 need: 1,
249 have: 0,
250 what: "answ answ_id",
251 })?;
252 let answ_id = AnswId::from_u8(id_byte);
253 let text_chars = if answ_id == AnswId::Answer {
254 &body[1..]
255 } else {
256 &body[..0]
257 };
258 Ok(Self {
259 answ_id,
260 text_chars,
261 })
262 }
263}
264
265impl Serialize for Answ<'_> {
266 type Error = Error;
267 fn serialized_len(&self) -> usize {
268 let body = 1 + if self.answ_id == AnswId::Answer {
269 self.text_chars.len()
270 } else {
271 0
272 };
273 super::apdu_len(body)
274 }
275 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
276 let with_text = self.answ_id == AnswId::Answer;
277 let body_len = 1 + if with_text { self.text_chars.len() } else { 0 };
278 let mut pos = super::write_apdu_header(tag::ANSW, body_len, buf)?;
279 buf[pos] = self.answ_id.to_u8();
280 pos += 1;
281 if with_text {
282 buf[pos..pos + self.text_chars.len()].copy_from_slice(self.text_chars);
283 pos += self.text_chars.len();
284 }
285 Ok(pos)
286 }
287}
288
289impl<'a> ApduDef<'a> for Answ<'a> {
290 const TAG: ApduTag = tag::ANSW;
291 const NAME: &'static str = "ANSW";
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
300#[cfg_attr(feature = "serde", derive(serde::Serialize))]
301pub struct Menu<'a> {
302 pub more: bool,
304 pub choice_nb: u8,
306 #[cfg_attr(feature = "serde", serde(borrow))]
308 pub title: Text<'a>,
309 #[cfg_attr(feature = "serde", serde(borrow))]
311 pub subtitle: Text<'a>,
312 #[cfg_attr(feature = "serde", serde(borrow))]
314 pub bottom: Text<'a>,
315 #[cfg_attr(feature = "serde", serde(borrow))]
317 pub choices: Vec<Text<'a>>,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
324#[cfg_attr(feature = "serde", derive(serde::Serialize))]
325pub struct List<'a>(
326 #[cfg_attr(feature = "serde", serde(borrow))]
328 pub Menu<'a>,
329);
330
331impl<'a> Parse<'a> for List<'a> {
332 type Error = Error;
333 fn parse(bytes: &'a [u8]) -> Result<Self> {
334 Menu::parse_list(bytes).map(List)
335 }
336}
337
338impl Serialize for List<'_> {
339 type Error = Error;
340 fn serialized_len(&self) -> usize {
341 self.0.list_serialized_len()
342 }
343 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
344 self.0.serialize_list(buf)
345 }
346}
347
348impl<'a> ApduDef<'a> for List<'a> {
349 const TAG: ApduTag = tag::LIST_LAST;
350 const NAME: &'static str = "LIST";
351}
352
353impl<'a> Menu<'a> {
354 fn parse_with_tag(bytes: &'a [u8], expected: ApduTag, what: &'static str) -> Result<Self> {
355 let more = expected == tag::MENU_MORE || expected == tag::LIST_MORE;
356 let body = super::parse_apdu_header(bytes, expected, what)?;
357 let choice_nb = *body.first().ok_or(Error::BufferTooShort {
358 need: 1,
359 have: 0,
360 what,
361 })?;
362 let mut pos = 1usize;
363 let (title, n) = Text::parse_component(&body[pos..])?;
364 pos += n;
365 let (subtitle, n) = Text::parse_component(&body[pos..])?;
366 pos += n;
367 let (bottom, n) = Text::parse_component(&body[pos..])?;
368 pos += n;
369 let mut choices = Vec::new();
370 while pos < body.len() {
371 let (c, n) = Text::parse_component(&body[pos..])?;
372 pos += n;
373 choices.push(c);
374 }
375 Ok(Self {
376 more,
377 choice_nb,
378 title,
379 subtitle,
380 bottom,
381 choices,
382 })
383 }
384
385 fn body_len(&self) -> usize {
386 let mut n = 1
387 + self.title.component_len()
388 + self.subtitle.component_len()
389 + self.bottom.component_len();
390 for c in &self.choices {
391 n += c.component_len();
392 }
393 n
394 }
395
396 fn serialize_with_tag(&self, t: ApduTag, buf: &mut [u8]) -> Result<usize> {
397 let body_len = self.body_len();
398 let mut pos = super::write_apdu_header(t, body_len, buf)?;
399 buf[pos] = self.choice_nb;
400 pos += 1;
401 pos += self.title.serialize_component(&mut buf[pos..])?;
402 pos += self.subtitle.serialize_component(&mut buf[pos..])?;
403 pos += self.bottom.serialize_component(&mut buf[pos..])?;
404 for c in &self.choices {
405 pos += c.serialize_component(&mut buf[pos..])?;
406 }
407 Ok(pos)
408 }
409
410 #[must_use]
412 pub fn menu_tag(&self) -> ApduTag {
413 if self.more {
414 tag::MENU_MORE
415 } else {
416 tag::MENU_LAST
417 }
418 }
419
420 #[must_use]
423 pub fn list_tag(&self) -> ApduTag {
424 if self.more {
425 tag::LIST_MORE
426 } else {
427 tag::LIST_LAST
428 }
429 }
430
431 pub fn parse_list(bytes: &'a [u8]) -> Result<Self> {
433 if bytes.len() < 3 {
434 return Err(Error::BufferTooShort {
435 need: 3,
436 have: bytes.len(),
437 what: "list tag",
438 });
439 }
440 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
441 let expected = if t == tag::LIST_MORE {
442 tag::LIST_MORE
443 } else {
444 tag::LIST_LAST
445 };
446 Self::parse_with_tag(bytes, expected, "list")
447 }
448
449 #[must_use]
451 pub fn list_serialized_len(&self) -> usize {
452 super::apdu_len(self.body_len())
453 }
454
455 pub fn serialize_list(&self, buf: &mut [u8]) -> Result<usize> {
457 self.serialize_with_tag(self.list_tag(), buf)
458 }
459}
460
461impl<'a> Parse<'a> for Menu<'a> {
462 type Error = Error;
463 fn parse(bytes: &'a [u8]) -> Result<Self> {
464 if bytes.len() < 3 {
465 return Err(Error::BufferTooShort {
466 need: 3,
467 have: bytes.len(),
468 what: "menu tag",
469 });
470 }
471 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
472 let expected = if t == tag::MENU_MORE {
473 tag::MENU_MORE
474 } else {
475 tag::MENU_LAST
476 };
477 Self::parse_with_tag(bytes, expected, "menu")
478 }
479}
480
481impl Serialize for Menu<'_> {
482 type Error = Error;
483 fn serialized_len(&self) -> usize {
484 super::apdu_len(self.body_len())
485 }
486 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
487 self.serialize_with_tag(self.menu_tag(), buf)
488 }
489}
490
491impl<'a> ApduDef<'a> for Menu<'a> {
492 const TAG: ApduTag = tag::MENU_LAST;
493 const NAME: &'static str = "MENU";
494}
495
496#[derive(Debug, Clone, Copy, PartialEq, Eq)]
499#[cfg_attr(feature = "serde", derive(serde::Serialize))]
500pub struct MenuAnsw {
501 pub choice_ref: u8,
503}
504
505impl<'a> Parse<'a> for MenuAnsw {
506 type Error = Error;
507 fn parse(bytes: &'a [u8]) -> Result<Self> {
508 let body = super::parse_apdu_header(bytes, tag::MENU_ANSW, "menu_answ")?;
509 let choice_ref = *body.first().ok_or(Error::BufferTooShort {
510 need: 1,
511 have: 0,
512 what: "menu_answ choice_ref",
513 })?;
514 Ok(Self { choice_ref })
515 }
516}
517
518impl Serialize for MenuAnsw {
519 type Error = Error;
520 fn serialized_len(&self) -> usize {
521 super::apdu_len(1)
522 }
523 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
524 let mut pos = super::write_apdu_header(tag::MENU_ANSW, 1, buf)?;
525 buf[pos] = self.choice_ref;
526 pos += 1;
527 Ok(pos)
528 }
529}
530
531impl ApduDef<'_> for MenuAnsw {
532 const TAG: ApduTag = tag::MENU_ANSW;
533 const NAME: &'static str = "MENU_ANSW";
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 fn text(more: bool, s: &[u8]) -> Text<'_> {
541 Text {
542 more,
543 text_chars: s,
544 }
545 }
546
547 #[test]
548 fn text_round_trips_and_more_bites() {
549 let t = text(false, b"HELLO");
550 let bytes = t.to_bytes();
551 assert_eq!(
552 bytes,
553 [0x9F, 0x88, 0x03, 0x05, b'H', b'E', b'L', b'L', b'O']
554 );
555 assert_eq!(Text::parse(&bytes).unwrap(), t);
556 let mut other = t.clone();
558 other.more = true;
559 let ob = other.to_bytes();
560 assert_eq!(ob[2], 0x04);
561 assert_ne!(bytes, ob);
562 }
563
564 #[test]
565 fn text_empty_is_null_object() {
566 let t = text(false, b"");
567 let bytes = t.to_bytes();
568 assert_eq!(bytes, [0x9F, 0x88, 0x03, 0x00]);
569 assert_eq!(Text::parse(&bytes).unwrap(), t);
570 }
571
572 #[test]
573 fn enq_pin_example_round_trips() {
574 let prompt = b"PLEASE TYPE YOUR PIN CODE";
576 let e = Enq {
577 blind_answer: true,
578 answer_text_length: 4,
579 text_chars: prompt,
580 };
581 let bytes = e.to_bytes();
582 assert_eq!(&bytes[..3], &[0x9F, 0x88, 0x07]);
584 assert_eq!(bytes[4], 0xFF); assert_eq!(bytes[5], 0x04);
586 let parsed = Enq::parse(&bytes).unwrap();
587 assert_eq!(parsed, e);
588 assert!(parsed.blind_answer);
589 let mut other = e.clone();
591 other.blind_answer = false;
592 let ob = other.to_bytes();
593 assert_eq!(ob[4], 0xFE);
594 assert_ne!(bytes, ob);
595 }
596
597 #[test]
598 fn answ_answer_and_cancel() {
599 let a = Answ {
600 answ_id: AnswId::Answer,
601 text_chars: b"1234",
602 };
603 let bytes = a.to_bytes();
604 assert_eq!(
605 bytes,
606 [0x9F, 0x88, 0x08, 0x05, 0x01, b'1', b'2', b'3', b'4']
607 );
608 assert_eq!(Answ::parse(&bytes).unwrap(), a);
609
610 let c = Answ {
612 answ_id: AnswId::Cancel,
613 text_chars: &[],
614 };
615 let cb = c.to_bytes();
616 assert_eq!(cb, [0x9F, 0x88, 0x08, 0x01, 0x00]);
617 assert_eq!(Answ::parse(&cb).unwrap(), c);
618 assert_eq!(c.answ_id.name(), "cancel");
619
620 let mut other = a.clone();
622 other.text_chars = b"9999";
623 assert_ne!(bytes, other.to_bytes());
624 }
625
626 #[test]
627 fn menu_two_choice_example_round_trips_and_bites() {
628 let m = Menu {
631 more: false,
632 choice_nb: 2,
633 title: text(false, b"DO YOU WANT TO BUY?"),
634 subtitle: text(false, b"JURASSIC PARK"),
635 bottom: text(false, b""),
636 choices: alloc::vec![text(false, b"YES"), text(false, b"NO")],
637 };
638 let bytes = m.to_bytes();
639 assert_eq!(&bytes[..3], &[0x9F, 0x88, 0x09]);
640 let parsed = Menu::parse(&bytes).unwrap();
641 assert_eq!(parsed, m);
642 assert_eq!(parsed.choices.len(), 2);
643 assert_eq!(parsed.title.text_chars, b"DO YOU WANT TO BUY?");
644
645 assert_eq!(bytes[3], 58);
648
649 let mut other = m.clone();
651 other.choice_nb = 0xFF;
652 assert_ne!(bytes, other.to_bytes());
653
654 let mut more = m.clone();
656 more.more = true;
657 let mb = more.to_bytes();
658 assert_eq!(mb[2], 0x0A);
659 assert_ne!(bytes, mb);
660 }
661
662 #[test]
663 fn list_uses_list_tags_and_round_trips() {
664 let l = List(Menu {
665 more: false,
666 choice_nb: 0xFF, title: text(false, b"ENTITLEMENTS"),
668 subtitle: text(false, b""),
669 bottom: text(false, b""),
670 choices: alloc::vec![text(false, b"A"), text(false, b"B"), text(false, b"C")],
671 });
672 let bytes = l.to_bytes();
673 assert_eq!(&bytes[..3], &[0x9F, 0x88, 0x0C]);
674 let parsed = List::parse(&bytes).unwrap();
675 assert_eq!(parsed, l);
676 assert_eq!(parsed.0.choices.len(), 3);
677
678 let mut more = l.clone();
680 more.0.more = true;
681 let mb = more.to_bytes();
682 assert_eq!(mb[2], 0x0D);
683 assert_eq!(List::parse(&mb).unwrap(), more);
684 assert_ne!(bytes, mb);
685 }
686
687 #[test]
688 fn menu_answ_round_trips_and_bites() {
689 let a = MenuAnsw { choice_ref: 0x02 };
690 let bytes = a.to_bytes();
691 assert_eq!(bytes, [0x9F, 0x88, 0x0B, 0x01, 0x02]);
692 assert_eq!(MenuAnsw::parse(&bytes).unwrap(), a);
693 let other = MenuAnsw { choice_ref: 0x00 };
694 assert_ne!(bytes, other.to_bytes());
695 }
696}