Skip to main content

scll_core/
limits.rs

1//! Central capacity constants for the `no_std` + `heapless` build (PDD §2.2,
2//! short-APDU-only). Every former `Vec`/`String` in the public surface is a
3//! fixed-capacity `heapless` collection sized from one of these constants.
4//!
5//! Two tiers:
6//!   * **Wire-level (normative).** Derived from ISO/GP limits — do NOT tune;
7//!     changing them breaks spec conformance.
8//!   * **Product/config.** Bounded by card *population*, not by any spec.
9//!     Tune per target. Promote to const-generic parameters later if a single
10//!     library build must serve cards of very different sizes.
11
12// =========================================================================
13// Wire-level (normative — do not tune)
14// =========================================================================
15
16/// Max AID length. RID (5) + PIX (≤11). ISO/IEC 7816-5; AID structure also
17/// ISO/IEC 7816-4:2020 §8.
18pub const AID_MAX: usize = 16;
19
20/// Max short C-APDU on the wire: 4 header + 1 Lc + 255 data + 1 Le.
21/// Extended APDUs are out of scope (PDD §2.2). ISO/IEC 7816-4:2020 §5.1.
22pub const CAPDU_MAX: usize = 261;
23
24/// Max short R-APDU: 256 response-data bytes (Le = `00`) + 2 SW.
25/// ISO/IEC 7816-4:2020 §5.1.
26pub const RAPDU_MAX: usize = 258;
27
28/// ATR (contact) ≤ 33 B (ISO/IEC 7816-3 §8); ATS (contactless) ≤ 254 B via a
29/// 1-byte length prefix (ISO/IEC 14443-4). 255 covers both.
30pub const ATR_ATS_MAX: usize = 255;
31
32/// Max Load File Data Block hash. SHA-512 = 64 B (largest of SHA-1/256/384/512
33/// permitted by GPCS v2.3.1 §11.6; SHA-256 is the library default).
34pub const HASH_MAX: usize = 64;
35
36/// Max GET DATA response payload (bounded by the short-APDU response field).
37/// Used for raw CRD `'66'`, CCI `'67'`, and key-template `'00E0'` captures.
38pub const GETDATA_RAW_MAX: usize = 256;
39
40/// Max INSTALL command data field (bounded by short Lc). GPCS v2.3.1 §11.5.
41pub const INSTALL_PARAMS_MAX: usize = 255;
42
43/// Issuer Identification Number object (`'0042'`). ISO/IEC 7812 — short BCD;
44/// 16 B is safe headroom.
45pub const IIN_MAX: usize = 16;
46
47/// Card Image Number object (`'0045'`). Short; 16 B headroom.
48pub const CIN_MAX: usize = 16;
49
50/// Max plaintext key length (AES-256). Amendment D v1.1.2 §4.1. Sizes the
51/// [`crate::backend::ExportedKey`] buffer and software-backend key storage.
52pub const KEY_BYTES_MAX: usize = 32;
53
54/// Encrypted key block value in a PUT KEY payload (the new key encrypted under
55/// the DEK). AES-256 = 32 B (block-multiple, no extra pad); 3DES double = 16 B.
56/// Amendment D v1.1.2 §4.1 / GPCS v2.3.1 §11.8. Returned by
57/// `*_encrypt_put_key_payload`.
58pub const ENC_KEY_BLOCK_MAX: usize = 32;
59
60/// GP Key Check Value length. GPCS v2.3.1 §E / Amendment D §4.1.4.
61pub const KCV_LEN: usize = 3;
62
63/// SCP03 MAC chaining value / SCP02 ICV length (one AES/3DES block padded).
64/// Backend-side only; listed here for the session-state sizing note.
65pub const MAC_CHAIN_LEN: usize = 16;
66
67/// Max SCP03 challenge / cryptogram / appended-MAC field length. 8 bytes in S8
68/// mode, 16 bytes in S16 mode (Amendment D v1.2 §6.2.x). Bounds the
69/// `heapless::Vec` returned by the cryptogram / pseudo-challenge backend
70/// methods, and the parsed challenge/cryptogram fields in the IU response.
71pub const SCP03_S16_MAX: usize = 16;
72
73// =========================================================================
74// Product / config (tune to the target card population)
75// =========================================================================
76
77/// Distinct SCP variants a card may advertise in CRD `'64'`.
78pub const MAX_SCP_VARIANTS: usize = 8;
79
80/// Keysets (by KVN) tracked in the ISD key inventory.
81pub const MAX_KEYSETS: usize = 16;
82
83/// Keys per keyset (KID slots; typically 3 — ENC/MAC/DEK).
84pub const MAX_KEYS_PER_SET: usize = 16;
85
86/// Cipher algorithms advertised in CCI `'A1'`.
87pub const MAX_CIPHERS: usize = 16;
88
89/// Raw privilege bytes captured from CCI (privileges are 3 B / 24 bits).
90pub const MAX_PRIVILEGE_BYTES: usize = 8;
91
92/// Security Domains held in a `CardInventory` snapshot.
93pub const MAX_SDS: usize = 8;
94
95/// Applet instances held in a `CardInventory` snapshot. Sized for a populated
96/// `JCOP`-class card (the `gp --list` reference dump has ~30 ELFs + several
97/// applets); a card reporting more yields `WarningKind::InventoryTruncated`
98/// (PDD §5.12a), never an error.
99pub const MAX_APPLETS: usize = 48;
100
101/// Executable Load Files held in a `CardInventory` snapshot. Raised from 16 in
102/// S7a: the reference `gp --list` dump alone has ~30 `PKG:` lines, so 16 would
103/// always truncate a real card. Overflow ⇒ `WarningKind::InventoryTruncated`.
104pub const MAX_ELFS: usize = 32;
105
106/// Top-level `'E3'` registry entries decoded from **one** GET STATUS response
107/// page by `parse_status_registry` (PDD §5.12a). A short-APDU page is ≤256 B and
108/// the smallest `'E3'` entry is ~7 B, so ≤ ~36 fit; 64 is generous headroom.
109/// The per-scope inventory caps (`MAX_SDS` / `MAX_APPLETS` / `MAX_ELFS`) bound
110/// the *accumulated* result across pages; this only bounds a single page.
111pub const MAX_REGISTRY_ENTRIES: usize = 64;
112
113/// Maximum GET STATUS pages (`63 10` continuations) read per P1 scope before
114/// `get_card_inventory` stops and flags `InventoryTruncated` (PDD §5.12a). A
115/// guard against a card that loops on `63 10`; 16 pages × ~256 B comfortably
116/// covers any short-APDU registry.
117pub const MAX_STATUS_PAGES: usize = 16;
118
119/// Raw-byte accumulator for one GET STATUS scope across all its `63 10`
120/// continuations (PDD §5.12a; GPCS v2.3.1 §11.4 Table 11-38). The response is
121/// chained at the **byte** level — a `'E3'` entry, or even a single nested
122/// value inside it (e.g. a module AID), can be split exactly at the page
123/// boundary — so each page's raw bytes are concatenated and the whole scope
124/// is parsed once, rather than parsing every page independently. Sized as
125/// `MAX_STATUS_PAGES × RAPDU_MAX` (16 × 258 = 4128 B), the worst case if every
126/// page were maximally full; a card exceeding this is capped, same
127/// "valid prefix, never an error" contract as the other inventory limits.
128pub const MAX_STATUS_SCOPE_BYTES: usize = MAX_STATUS_PAGES * RAPDU_MAX;
129
130/// Class (module) AIDs inside one ELF entry.
131pub const MAX_MODULES_PER_ELF: usize = 16;
132
133/// Objects reported removed by a cascade DELETE (`instances_removed`/`elfs_removed`).
134pub const MAX_REMOVED_OBJECTS: usize = 32;
135
136/// Imported package AIDs (dependencies) parsed from a CAP `Import.cap`.
137pub const MAX_CAP_IMPORTS: usize = 16;
138
139/// Applet class entries declared in one CAP `Applet.cap`.
140pub const MAX_CAP_APPLETS: usize = 8;
141
142/// Non-fatal warnings attached to any one report.
143pub const MAX_WARNINGS: usize = 16;
144
145/// Card quirk strings collected during discovery.
146pub const MAX_QUIRKS: usize = 8;
147
148/// Distinct GP key-type bytes in `ScllError::KeyTypeUnsupported.supported`.
149pub const MAX_KEYTYPE_SUPPORTED: usize = 16;
150
151/// Top-level BER-TLV objects returned by one `tlv::parse` call. Bounds the
152/// parser's output list; denser input → `TlvError::TooMany` (no panic, §10.5).
153/// A 256-byte response holds at most 128 minimal (2-byte) TLVs; 64 is generous
154/// for real GP templates (`'66'`/`'67'`/`'E3'`/`'00E0'`).
155pub const MAX_TLVS: usize = 64;
156
157// =========================================================================
158// Diagnostic string caps (heapless::String<N>)
159// =========================================================================
160
161/// Transport name (`"pcsc" | "jcsim" | "user"`). Could also be `&'static str`.
162pub const TRANSPORT_NAME_MAX: usize = 16;
163
164/// `Warning.detail` text.
165pub const WARNING_DETAIL_MAX: usize = 128;
166
167/// Free-form `Other(..)` detail (`CipherAlg` / `DiscoveryWarning` / `TransportError`).
168/// Prefer typed variants over this where possible.
169pub const OTHER_DETAIL_MAX: usize = 64;
170
171// =========================================================================
172// LOAD / CAP — streaming, STORED + DEFLATE (PDD §5.4a / §9)
173// =========================================================================
174
175/// LOAD command data payload per block, sized so the wrapped command always
176/// fits a short APDU (Lc ≤ 255) under the most expensive secure-messaging mode
177/// the backend can negotiate. Under C-DECRYPTION the chunk is first pad-`80`
178/// (ISO 7816-4) padded up to the cipher block size (16 B SCP03 / 8 B SCP02) —
179/// padding adds 1..block bytes, so an already block-aligned chunk grows by a
180/// *whole* extra block — then a C-MAC is appended (8 B in SCP03-S8 / SCP02,
181/// 16 B in SCP03-S16). Worst case (SCP03-S16): ceil16(chunk+1) + 16 ≤ 255 ⇒
182/// chunk ≤ 223. The CAP parser yields the assembled LFDB (GPCS §C.2 order) in
183/// chunks of this size; nothing holds the whole multi-KB block in RAM.
184/// PDD §5.4a / §9; LOAD is GPCS v2.3.1 §11.6 (SCP03 wrap: Amendment D §6.2.3–6.2.4).
185pub const LOAD_BLOCK_DATA: usize = 223;
186
187/// DEFLATE sliding-window / dictionary size for inflating compressed CAP
188/// components (RFC 1951 max back-reference distance = 32 KiB). The caller lends
189/// a buffer of this size to `miniz_oxide` (alloc-free, `default-features =
190/// false`); it doubles as the LZ77 dictionary, so no separate allocation is
191/// needed. Must be a power of two. PDD §5.4a.
192pub const INFLATE_WINDOW: usize = 32 * 1024;