Skip to main content

scll_core/command/
select.rs

1//! SELECT (CLA 00, INS A4, P1 04, P2 00) — PDD §5.2/§5.9.
2
3use crate::command::{build, BuildError, Capdu};
4
5/// Build a SELECT-by-AID. Empty `aid` selects the default application (ISD on a
6/// GP-compliant card, GPCS v2.3.1 §5.2.2).
7///
8/// `CLA=00 INS=A4 P1=04` (select by name, first/only occurrence) `P2=00`.
9/// Empty `aid` ⇒ Case 2S `00 A4 04 00 00`; a 5..=16-byte AID ⇒ Case 4S
10/// `00 A4 04 00 Lc <aid> 00`.
11///
12/// # Errors
13/// Returns [`BuildError::Overflow`] if the encoded inputs would exceed the
14/// short-APDU plaintext buffer (`CAPDU_MAX`).
15#[allow(clippy::module_name_repetitions)] // GP command name; intentional public API
16pub fn select_by_aid(aid: &[u8]) -> Result<Capdu, BuildError> {
17    build(0x00, 0xA4, 0x04, 0x00, aid, true)
18}
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23    use proptest::prelude::*;
24    use scll_test_util::HexSlice;
25
26    #[test]
27    fn empty_aid_selects_default_application() {
28        // GPCS §5.2.2: empty SELECT returns the default-selected application.
29        let apdu = select_by_aid(&[]).unwrap();
30        assert_eq!(HexSlice(&apdu), HexSlice([0x00, 0xA4, 0x04, 0x00, 0x00]));
31    }
32
33    #[test]
34    fn by_aid_wraps_header_lc_and_le() {
35        // Canonical ISD RID shape (GPCS A000000151…), 5-byte AID.
36        let aid = [0xA0, 0x00, 0x00, 0x01, 0x51];
37        let apdu = select_by_aid(&aid).unwrap();
38        assert_eq!(
39            HexSlice(&apdu),
40            HexSlice([0x00, 0xA4, 0x04, 0x00, 0x05, 0xA0, 0x00, 0x00, 0x01, 0x51, 0x00])
41        );
42    }
43
44    #[test]
45    fn oversized_input_returns_overflow_not_panic() {
46        // 256 bytes cannot fit a 1-byte short Lc → total, no panic.
47        let big = [0xABu8; 256];
48        assert_eq!(select_by_aid(&big), Err(BuildError::Overflow));
49    }
50
51    proptest! {
52        /// Any short-length AID frames a Case-4S APDU with a matching Lc/body.
53        #[test]
54        fn round_trips_any_short_aid(aid in proptest::collection::vec(any::<u8>(), 1..=255)) {
55            let apdu = select_by_aid(&aid).unwrap();
56            prop_assert_eq!(&apdu[0..4], &[0x00, 0xA4, 0x04, 0x00]);
57            prop_assert_eq!(usize::from(apdu[4]), aid.len());
58            prop_assert_eq!(&apdu[5..5 + aid.len()], aid.as_slice());
59            prop_assert_eq!(*apdu.last().unwrap(), 0x00);
60        }
61
62        /// Total: never panics for any input length, oversized included.
63        #[test]
64        fn never_panics(aid in proptest::collection::vec(any::<u8>(), 0..400)) {
65            let _ = select_by_aid(&aid);
66        }
67    }
68}