scll_core/command/get_status.rs
1//! GET STATUS (CLA 80, INS F2) — PDD §5.12, GPCS v2.3.1 §11.4.
2//!
3//! Card/ISD scope: P1 `0x80` (Table 11-33), P2 `0x02` (Table 11-34: b2=1 modern
4//! TLV, b1=0 first/all), Data `'4F' 00` match-all (Table 11-35). Response is a
5//! `'E3'` registry entry carrying `'9F70'` life-cycle (len 1), `'4F'` AID,
6//! `'C5'` privileges (Table 11-36, §11.4.3.1) — **verified**.
7
8use crate::command::{build, push, push_lv, BuildError, Capdu};
9
10/// P2 = get **first/all** occurrence(s) + modern TLV response (Table 11-34:
11/// b1=0 first/all, b2=1 TLV). The opening value of a paged GET STATUS.
12pub const P2_FIRST_TLV: u8 = 0x02;
13/// P2 = get **next** occurrence + modern TLV response (Table 11-34: b1=1 next,
14/// b2=1 TLV). Sent to continue after a `63 10` "more data" warning
15/// (Table 11-38) when enumerating the registry across pages (PDD §5.12a).
16pub const P2_NEXT_TLV: u8 = 0x03;
17
18/// Build a GET STATUS for an arbitrary P1 scope and `'4F'`-tagged AID qualifier,
19/// with P2 fixed at [`P2_FIRST_TLV`] (`0x02`).
20///
21/// `CLA=80 INS=F2`, Data = `'4F' len <aid_qualifier>` (Table 11-35; `'4F'` is
22/// Mandatory). An empty `aid_qualifier` yields `'4F' 00` = match-all
23/// (§11.4.2.3). `Le=00`. Thin wrapper over [`get_status_p2`].
24///
25/// # Errors
26/// Returns [`BuildError::Overflow`] if the encoded inputs would exceed the
27/// short-APDU plaintext buffer (`CAPDU_MAX`).
28pub fn get_status(p1: u8, aid_qualifier: &[u8]) -> Result<Capdu, BuildError> {
29 get_status_p2(p1, P2_FIRST_TLV, aid_qualifier)
30}
31
32/// Build a GET STATUS with an explicit P2 — the paginating form used by
33/// `get_card_inventory` (PDD §5.12a) to send [`P2_FIRST_TLV`] for the first page
34/// and [`P2_NEXT_TLV`] to continue after `63 10`.
35///
36/// `CLA=80 INS=F2`, Data = `'4F' len <aid_qualifier>` (Table 11-35), `Le=00`.
37/// P1/P2 are passed through verbatim; the caller owns their meaning
38/// (Tables 11-33 / 11-34).
39///
40/// # Errors
41/// Returns [`BuildError::Overflow`] if the encoded inputs would exceed the
42/// short-APDU plaintext buffer (`CAPDU_MAX`).
43pub fn get_status_p2(p1: u8, p2: u8, aid_qualifier: &[u8]) -> Result<Capdu, BuildError> {
44 let mut data = Capdu::new();
45 push(&mut data, &[0x4F])?;
46 push_lv(&mut data, aid_qualifier)?;
47 build(0x80, 0xF2, p1, p2, &data, true)
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use scll_test_util::HexSlice;
54
55 #[test]
56 fn isd_match_all_is_the_verified_4f00_form() {
57 // PDD §5.12 verified ISD GET STATUS: 80 F2 80 02 02 4F00 00.
58 let apdu = get_status(0x80, &[]).unwrap();
59 assert_eq!(
60 HexSlice(&apdu),
61 HexSlice([0x80, 0xF2, 0x80, 0x02, 0x02, 0x4F, 0x00, 0x00])
62 );
63 }
64
65 #[test]
66 fn qualifier_is_tlv_tagged_under_4f() {
67 // Applications/SSD scope P1=0x40 with a partial-AID search qualifier.
68 let apdu = get_status(0x40, &[0xA0, 0x00]).unwrap();
69 assert_eq!(
70 HexSlice(&apdu),
71 HexSlice([0x80, 0xF2, 0x40, 0x02, 0x04, 0x4F, 0x02, 0xA0, 0x00, 0x00])
72 );
73 }
74
75 #[test]
76 fn oversized_qualifier_overflows_lc() {
77 // '4F' + len + 255 bytes = 257-byte data field → exceeds short Lc.
78 let big = [0x11u8; 255];
79 assert_eq!(get_status(0x80, &big), Err(BuildError::Overflow));
80 }
81
82 #[test]
83 fn p2_next_form_paginates_with_03() {
84 // The §5.12a continuation: same scope, P2 = 0x03 (next + TLV).
85 let apdu = get_status_p2(0x40, P2_NEXT_TLV, &[]).unwrap();
86 assert_eq!(
87 HexSlice(&apdu),
88 HexSlice([0x80, 0xF2, 0x40, 0x03, 0x02, 0x4F, 0x00, 0x00])
89 );
90 }
91
92 #[test]
93 fn get_status_is_the_first_tlv_form_of_p2() {
94 // get_status(..) must equal get_status_p2(.., 0x02, ..) for every scope.
95 for p1 in [0x80u8, 0x40, 0x20, 0x10] {
96 assert_eq!(get_status(p1, &[]), get_status_p2(p1, P2_FIRST_TLV, &[]));
97 }
98 }
99}