Document status: working draft submitted to the CNA TC Terminal for validation.
Preamble
The purpose of this document is to present all the evolutions of the Terminal APIs between the Keypop Java versions currently in production and the current specifications of the CNA Terminal APIs — for validation by the members of the TC Terminal (Technical Committee Terminal) of the Calypso Networks Association (CNA).
Compared versions
The “before” reference consists of the latest published versions of the Keypop Java modules (production tags). The “after” reference consists of the normative specifications (index.adoc) and the associated class diagrams (uml/class-diagram.puml) of each calypsonet-terminal-*-uml-api repository.
| API | Specification reference | Keypop Java version in production (before) | Specified version (after) |
|---|---|---|---|
| Terminal Reader API | CNA-TR-API | keypop-reader-java-api 2.1.0 | 3.0.0 |
| Terminal Card API (internal) | CNA-TC-API | keypop-card-java-api 2.0.1 | 3.0.0 |
| Terminal Calypso Card API | CNA-TCC-API | keypop-calypso-card-java-api 2.2.0 | 3.0.0 |
| Terminal Reader Definitions API (new) | CNA-TRD-API | — | 1.0.0 |
| Terminal Calypso Crypto Legacy SAM API | CNA-TCCL-API | keypop-calypso-crypto-legacysam-java-api 1.0.0 | 2.0.0 |
| Terminal Calypso Crypto Symmetric API | CNA-TCCS-API | keypop-calypso-crypto-symmetric-java-api 0.1.1 | 0.2.0 |
| Terminal Calypso Crypto Asymmetric API | CNA-TCCA-API | keypop-calypso-crypto-asymmetric-java-api 0.2.0 | 0.3.0 |
| Terminal Generic Card API | CNA-TGC-API | keypop-genericcard-jvm-api 1.0.0 | 2.0.0 |
| Terminal Storage Card API | CNA-TSC-API | keypop-storagecard-java-api 1.2.0 | 2.0.0 |
All the specifications are currently in …-SNAPSHOT versions.
For each evolution theme, it describes:
- the motivation (the “why”);
- the detailed changes in each of the APIs concerned;
- the design rationale (the “why this choice rather than another”).
Annex A additionally provides an exhaustive, API-by-API mapping between each element of the Java versions in production and what becomes of it in the specifications.
Alignment of the Keypop Java implementations with these versions and the writing of a technical migration guide for integrators will take place subsequently, after validation by the TC Terminal (see §18).
API visibility with respect to audiences
- The Reader API, the Calypso Card API, the Terminal Reader Definitions API, the Legacy SAM API, the Generic Card API and the Storage Card API are public APIs, directly manipulated by the integrator (application code).
- The Card API is an internal API: it serves as an integration contract between reader implementations and card extensions. The integrator does not have access to it.
- The Crypto Symmetric API and Crypto Asymmetric API define the contracts (SPI) between the Calypso Card API and the cryptographic modules; the integrator only uses them indirectly, through the crypto modules it instantiates (for example the Legacy SAM API).
This document describes the evolutions of all impacted APIs because they are coupled at the design level. The evolutions of the internal and contract APIs require no action from the integrator; they are absorbed by the Keypop implementations.
New API: Terminal Reader Definitions API
Version 3.0.0 introduces a new foundation API dedicated to hosting cross-cutting enumerated types of the Terminal APIs. It is created on the occasion of Theme 6 to host
RfTechnologyandCardType, but its scope is broader: it is intended to potentially host other enumerations that constitute global constants shared by several Terminal APIs. The Terminal Reader API now depends on this new API.Concretely, this translates into:
- a new repository:
calypsonet-terminal-reader-definitions-uml-api(version1.0.0-SNAPSHOT);- a new Keypop Java module:
keypop-reader-definitions-jvm-api(to be created, in accordance with the Keypop naming convention);- a declared dependency of the
keypop-reader-java-apimodule on this new module.
New form of the deliverables: language-independent normative specifications
Until now, the Terminal APIs were described by a UML diagram and by the Javadoc of the Keypop Java modules. Each API now has a normative specification (
index.adoc), written in a notation independent of the implementation language (inspired by Kotlin), together with its class diagram. This new design proposal aims to broaden the choice of implementation languages for the Terminal APIs beyond Java, for example Kotlin Multiplatform (KMP), Rust, Swift or C#. This change of form affects the way types and operations are expressed; the consequences are described in Theme 10 (§11).
Reference documents
Each calypsonet-terminal-*-uml-api repository hosted on github.com/calypsonet contains, at its root:
- the normative specification
index.adoc; - the class diagram
uml/class-diagram.puml, whose content is strictly aligned with the specification.
| Module | Repository | Version |
|---|---|---|
| Terminal Reader API | calypsonet-terminal-reader-uml-api | 3.0.0-SNAPSHOT |
| Terminal Card API (internal) | calypsonet-terminal-card-uml-api | 3.0.0-SNAPSHOT |
| Terminal Calypso Card API | calypsonet-terminal-calypso-card-uml-api | 3.0.0-SNAPSHOT |
| Terminal Reader Definitions API (new) | calypsonet-terminal-reader-definitions-uml-api | 1.0.0-SNAPSHOT |
| Terminal Calypso Crypto Legacy SAM API | calypsonet-terminal-calypso-crypto-legacysam-uml-api | 2.0.0-SNAPSHOT |
| Terminal Calypso Crypto Symmetric API | calypsonet-terminal-calypso-crypto-symmetric-uml-api | 0.2.0-SNAPSHOT |
| Terminal Calypso Crypto Asymmetric API | calypsonet-terminal-calypso-crypto-asymmetric-uml-api | 0.3.0-SNAPSHOT |
| Terminal Generic Card API | calypsonet-terminal-genericcard-uml-api | 2.0.0-SNAPSHOT |
| Terminal Storage Card API | calypsonet-terminal-storagecard-uml-api | 2.0.0-SNAPSHOT |
The former diagrams published in the
…-SNAPSHOT/folders (api_class_diagram.svgandapi_class_diagram_diff.svg) reflect an intermediate state of the work and are no longer up to date; the reference is now theindex.adocspecification.
Diagram reading conventions:
- elements in blue are additions or modifications of the new version;
- elements in grey are under study (“work in progress”): they have never been implemented and are not part of the normative scope (see §17);
- green classes group data, constants, enumerations and errors;
- signatures follow the language-independent notation described in Theme 10 (
→ Self,T?,val property: Type = default, etc.).
Table of contents
- Overview
- Theme 1 — Support for multiple logical channels
- Theme 2 — Timing countermeasures: relay attack and card emulation
- Theme 3 — Simplified observation management
- Theme 4 — Knowledge of the current secure session state
- Theme 5 — Semantic improvements (renamings and removals)
- Theme 6 — Strict typing of RF technologies and card types (ECP support)
- Theme 7 — Command identification (
commandId) - Theme 8 — Standardised reader discovery and access (
CardReaderProvider) - Theme 9 — Redesign of the card selection model
- Theme 10 — Implementation-language-independent specification
- Theme 11 — Data exposed without computation and access to raw data
- Theme 12 — Stored Value (SV) operations
- Theme 13 — Tolerance of a missing file or record in a secure session
- Theme 14 — Crypto extensions and command interleaving
- Normative clarifications
- Elements under study
- Migration procedure
- Next steps and validation by the TC Terminal
1. Overview
The new generation of the Terminal APIs introduces compatibility breaks on all existing APIs, creates a new foundation API (Terminal Reader Definitions API), and comes with a change of form of the deliverables (language-independent normative specifications). The changes are grouped into fourteen themes:
| # | Theme | Reader | Card | Calypso Card | Definitions | Legacy SAM | Crypto Sym. | Crypto Asym. | Generic Card | Storage Card |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Multiple logical channels | ● | ● | ● | — | — | — | — | ● | — |
| 2 | Relay and emulation countermeasures | — | ● | ● | — | — | — | — | ● | ● |
| 3 | Simplified observation | ● | — | — | — | — | — | — | — | — |
| 4 | Current secure session state | — | — | ● | — | — | — | — | — | — |
| 5 | Semantic improvements | ● | ● | ● | — | ● | ● | ● | ● | ● |
| 6 | RF / card type typing (ECP) | ● | — | — | ● (creation) | — | — | — | — | — |
| 7 | Command identification (commandId) | — | — | ● | — | ● | — | — | ● | ● |
| 8 | Standardised reader discovery | ● | — | — | — | — | — | — | — | — |
| 9 | Redesign of the selection model | ● | — | — | — | — | — | — | — | — |
| 10 | Language-independent specification | ● | ● | ● | ● | ● | ● | ● | ● | ● |
| 11 | Data without computation, raw data | — | ● | ● | — | ● | ● | ● | — | ● |
| 12 | Stored Value operations | — | — | ● | — | — | — | — | — | — |
| 13 | Missing file or record tolerated | — | — | ● | — | — | — | — | — | — |
| 14 | Crypto extensions and interleaving | — | — | ● | — | ● | — | — | — | — |
Cross-cutting consequences:
- Removal of the entire deprecated legacy: all elements marked
@Deprecatedin the production versions are removed with no compatibility alternative (for exampleChannelControlin the Calypso Card API,TransactionManager.processCommands()in the Legacy SAM API,prepareReadSystemBlock()in the Storage Card API). - Removal of the “work in progress” elements that appeared in the former diagrams for prospective purposes without ever being implemented:
ReaderApiFactory.createMultichannelCardSelector(),MultichannelCardSelector,CardSelectionResult.getCardReader()(Reader API);AsymmetricCryptoSecuritySetting.authorizeAllTrustedCa(),authorizeOnlyConfiguredCa()andrevokeCa(byte[])(Calypso Card API). The elements still under study in the Legacy SAM API are listed in §17. - Complete disappearance of
ChannelControl(Reader API, Card API, Calypso Card API): closing the channel becomes an explicit operation (closeChannel,transmitCardRequestAndCloseChannel,processCommandsAndCloseChannel). - Disappearance of duplicate errors: the
InvalidCardResponseExceptionof thereader.selectionpackage, as well as the communication and status errors specific to the Calypso Card API and the Legacy SAM API, disappear in favour of the Reader API errors. - Recursive genericity dropped: all interfaces of the form
T extends X<T>(transaction managers, selectors, signature data) lose their type parameter; fluent chaining is expressed by theSelfreturn type (see Theme 10).
2. Theme 1 — Support for multiple logical channels
2.1 Motivation
Until now, the API implicitly assumed that only one logical channel was open at a time between the terminal and the card. Version 3.0.0 explicitly introduces the notion of multiple logical channels (ISO cards supporting several simultaneous application selections on distinct logical channels), with two objectives:
- to allow several applications of the same card to be selected and handled in parallel;
- to clearly distinguish, for each smart card, on which channel it is attached and whether it is still active.
The concrete trigger for this work is the arrival of a new CNA product, OpenSAM, whose usage model relies on the simultaneous coexistence of several security applications accessible in parallel on distinct logical channels. The detailed specifications of OpenSAM are covered by the CNA documentation dedicated to this product.
2.2 Reader API
Multi-channel selection
- New manager
MultichannelCardSelectionManager, obtained throughReaderApiFactory.createMultichannelCardSelectionManager(), whose operationprocessCardSelectionScenario(reader: CardReader, channelSelectionPolicy: ChannelSelectionPolicy) → MultichannelCardSelectionResultexecutes the scenario by placing each successful selection on its own logical channel. If the presented card does not support multi-channel, anInvalidCardResponseerror is raised at scenario execution time. This manager is part of the overall redesign of the selection described in Theme 9 (§10). - New enumeration
ChannelSelectionPolicy:ALLOW_BASIC_CHANNEL— allows the use of the basic channel (channel 0) in addition to the additional logical channels;LOGICAL_CHANNEL_ONLY— restricts the selection to the additional logical channels (channel 0 is not used).
- New result
MultichannelCardSelectionResult(cardType,smartCards): all the cards it exposes are active in parallel, each on its own channel. The scheduled mode (selection on card insertion) is not offered in multi-channel mode.
Channel knowledge at card level
- New operation
SmartCard.isActive() → Boolean— the card knows whether it is still active on its channel. - New operation
IsoSmartCard.isBasicChannel() → Boolean— indicates whether the card is attached to the basic channel or to an additional logical channel.
Multi-channel transaction management
The hierarchy of transaction managers (namespace reader.transaction.spi) is restructured into three levels:
CardTransactionManager (root interface, non-generic)
├─ IsoCardTransactionManager (new, ISO 7816-4 — carries the conversion to multi-channel)
└─ MultichannelCardTransactionManager (new, actual multi-channel)
CardTransactionManager(redesigned) — no longer generic; exposesprocessCommands() → Unit(without parameter). It is the common root interface of all transaction managers.IsoCardTransactionManager— new intermediate interface dedicated to ISO 7816-4 cards. It exposes a single operationasMultichannelCardTransactionManager() → MultichannelCardTransactionManager, which returns a multi-channel view of the manager. The conversion itself never fails: if the underlying card does not support multi-channel, theInvalidCardResponseerror is only raised when the commands are processed by the obtained manager (processCommands,processCommandsAndCloseChannel).MultichannelCardTransactionManager— new interface extendingCardTransactionManager; exposes:processCommandsAndCloseChannel() → Unit— processes the pending commands and, upon success, closes the channel;closeChannel() → Unit— explicit, idempotent closing of the channel.
Expected anchoring on the consumer API side:
- APIs targeting ISO 7816-4 cards that are not intrinsically multi-channel (Calypso Card, Generic Card) make their transaction manager extend
IsoCardTransactionManagerand access multi-channel on demand throughasMultichannelCardTransactionManager().- APIs targeting intrinsically multi-channel cards (the future Terminal OpenSAM API in particular) make their transaction manager extend
MultichannelCardTransactionManagerdirectly.
2.3 Card API
- New operation
SmartCardSpi.deactivate() → Unit— allows the reader to deactivate the card, so that the application immediately seesSmartCard.isActive()switch tofalse. - New SPI interface
MultichannelSmartCardSpi(extendsSmartCardSpi) withgetChannel() → Int. - Redesign of
ProxyReaderApi:- removed:
transmitCardRequest(CardRequestSpi, ChannelControl)andreleaseChannel(); - added:
transmitCardRequest(cardRequest: CardRequest, smartCard: SmartCardSpi) → CardResponse;transmitCardRequestAndCloseChannel(cardRequest: CardRequest, multichannelSmartCard: MultichannelSmartCardSpi) → CardResponse;closeChannel(multichannelSmartCard: MultichannelSmartCardSpi) → Unit.
- removed:
- Addition of the property
CardSelectionResponse.channel: Int— the selection response carries the channel number (0in single-channel mode). - Removal of
CardResponseApi.isLogicalChannelOpen()— made redundant by the new model.
2.3.1 Role of the card passed to the ProxyReaderApi
The SmartCardSpi / MultichannelSmartCardSpi parameter is not a mere vehicle for the channel number. It plays up to three roles:
- carrying the logical channel number (with
MultichannelSmartCardSpionly); - carrying the active state of the card, so that the reader checks that it is still active before any transmission — an inactive card causes the
CardBrokenCommunicationerror; - allowing the reader to deactivate the card (
SmartCardSpi.deactivate()), for example after a communication error or on explicit channel closure.
2.3.2 SmartCard lifecycle
The reader keeps the references to the SmartCard instances resulting from the last selection and deactivates them in four cases:
- on a new selection in single-channel mode;
- on an explicit channel closure request (
closeChannel,processCommandsAndCloseChannel); - on the call to
ObservableCardReader.endCardProcessing(); - on an error indicating that the card can no longer be reached (
CardCommunication,ReaderCommunication).
This contract, which only appeared as prose in the previous version of this document, is now normative: it is defined in the Reader API specification (description of SmartCard.isActive, SmartCard lifecycle section).
2.4 Calypso Card API
- The Calypso
TransactionManagernow extendsIsoCardTransactionManager(instead ofCardTransactionManager). Multi-channel access is performed without a dedicated operation: the integrator callsasMultichannelCardTransactionManager()and then usesprocessCommandsAndCloseChannel()/closeChannel().
2.5 Generic Card API
- The transaction manager (renamed
GenericCardTransactionManager, see Theme 5) now extendsIsoCardTransactionManager.
2.6 Rationale
“By parameter” control (ChannelControl.KEEP_OPEN / CLOSE_AFTER) relied on an implicit, global notion of a “single current channel”. In a multi-channel context, this model is ambiguous: which channel does CLOSE_AFTER apply to? Moving to a model where the target (the SmartCard(Spi)) is explicitly named in each call removes this ambiguity.
The three-level hierarchy allows each consumer API to anchor itself at the capability level that exactly matches its card model: on-demand conversion for cards for which multi-channel is only an optional capability, direct anchoring for intrinsically multi-channel cards.
3. Theme 2 — Timing countermeasures: relay attack and card emulation
3.1 Motivation
Two threats are detected through the duration of the exchanges, and the new versions introduce a single mechanism for both.
- A relay attack consists in relaying the dialogue with a card to a remote location, which makes a fraudulent operation possible without the cardholder’s knowledge. The relay adds a transmission delay: an abnormally long exchange can therefore reveal that the card is not actually present in front of the reader.
- Card emulation consists in having a generic RFID device answer in place of the expected card. Such a device processes the command in software, where the chip answers in hardware: an abnormally long exchange then reveals that the answer does not come from the expected product. This threat mainly concerns storage cards, which have no cryptographic mechanism. The new versions introduce a mechanism for measuring and bounding APDU exchange durations and for bounding the secure session duration.
Threat model
- Targeted attack surface: application-level attack (software relay of APDUs, card emulation by a generic device), as opposed to attacks at the physical RF transport level, which are covered by hardware countermeasures.
- Unit of the bounds and of the measured durations: the microsecond (
µs). The millisecond is too coarse for the shortest exchanges, in particular a storage card read, which takes about 2 ms. The effective resolution of the measurement depends on the implementation, which must document it. - Measurement location: the Terminal Reader API implementation measures the effective duration of each APDU exchange and compares it with the bound declared on the request. The Calypso duration bounds are declared in the Calypso Card API and each covers a single command exchange (Open Secure Session, Close Secure Session, SV Reload / SV Debit / SV Undebit).
- Behaviour after an overrun: the Card API raises the
ApduExchangeDurationExceedederror, which the higher-level extensions intercept and propagate to the application as anInvalidCardResponse. The Calypso Card API now specifies this behaviour: if a secure session is open, it is automatically cancelled before the error is propagated, so that no modification performed during the session is validated by the card; outside a session (SV command), there is nothing to cancel and only the error is propagated to the ticketing layer, which decides what to do according to its own context. In the Generic Card API, an overrun raisesInvalidCardResponse, whose message identifies the offending command.
3.2 Card API
- Request side:
ApduRequest.apduExchangeMaxDuration: Long? = null— maximum tolerated duration for the exchange (in microseconds);nullmeans “no bound”. - Response side:
ApduResponse.apduExchangeDuration: Long?— effective duration of the exchange;nullmeans “duration not measured”. - New error
ApduExchangeDurationExceeded— raised byProxyReaderApi.transmitCardRequest(...)when the effective duration exceeds the declared bound. Like the other APDU errors, it carriescardResponseandisCardResponseComplete. - The Card API specification now documents this mechanism as a practical solution for implementing anti-relay countermeasures (APDU exchange execution-time control chapter).
3.3 Calypso Card API
Each bound is declared according to two families of settings, each with a dedicated operation:
by CSN (
…ByCsn(maxDuration: Long, csnMin: Long)):csnMinis a threshold on the CSN (Calypso Serial Number, i.e. the Application Serial Number, compared as an unsigned 64-bit integer);by FCI (
…ByFci(maxDuration: Long, fciRegex: String)):fciRegexis a regular expression applied to the whole FCI returned by Select Application (excluding the status word), represented as an uppercase hexadecimal string without separators.New parent interface
SecuritySettings(calypso.card.transaction), extended bySymmetricCryptoSecuritySettingsandAsymmetricCryptoSecuritySettings. It carries the settings shared by every secure transaction, whatever the cryptographic nature of the session — four new operations:assignOpenSecureSessionMaxDurationByCsn(maxDuration: Long, csnMin: Long) → SelfandassignOpenSecureSessionMaxDurationByFci(maxDuration: Long, fciRegex: String) → Self— maximum duration of the Open Secure Session command exchange;assignCloseSecureSessionMaxDurationByCsn(maxDuration: Long, csnMin: Long) → SelfandassignCloseSecureSessionMaxDurationByFci(maxDuration: Long, fciRegex: String) → Self— maximum duration of the Close Secure Session command exchange.
SymmetricCryptoSecuritySettings— two new specific operations:assignSvCommandMaxDurationByCsn(maxDuration: Long, csnMin: Long) → SelfandassignSvCommandMaxDurationByFci(maxDuration: Long, fciRegex: String) → Self— maximum duration of the exchange of one of the SV Reload, SV Debit or SV Undebit commands.
Resolution rule (for a given card, per kind of bounded operation):
- CSN-based settings: each call defines a range bounded by its
csnMinand the immediately higher declaredcsnMin(or +∞). If the card’s CSN belongs to a range whosemaxDurationdiffers fromLong.MAX_VALUE, this value applies.- FCI-based settings: otherwise, the FCI-based settings are evaluated in declaration order and the first one whose expression matches the FCI applies; a
maxDurationequal toLong.MAX_VALUEthen means “no bound”.- Otherwise, no bound applies.
Consequences and details:
- CSN-based settings act as overrides: a default bound is expressed by a last FCI-based setting with the expression
.*, not by a CSN-based setting with the lowest threshold, which would shadow every FCI-based setting;Long.MAX_VALUEon a CSN range hands the decision back to the FCI-based settings for the cards of that range; - the match applies to the whole string (implicitly anchored at both ends); a byte is matched by
..; - to remain portable (Java, .NET, Swift/ICU, Rust), the expression is restricted to a common subset: literal characters,
., classes[...], quantifiers*,+,?,{n},{n,},{n,m}, alternation|and groups(...); backreferences, lookaround assertions, anchors and inline flags are excluded; - an invalid expression or one outside the subset, or a
maxDurationthat is not strictly positive, is rejected at call time (Argument pre-condition); - a new call with an already declared
csnMinreplaces the previous value; a new call with an identicalfciRegexreplaces the value while keeping its position in the evaluation order; - when no FCI is available, no FCI-based setting matches;
- FCI integrity: the FCI is obtained during the selection, outside any session, and is therefore not authenticated. If the expression filters on the startup info data, the integrator must execute a
prepareGetData(FCI_FOR_CURRENT_DF)inside the session to ensure the integrity of the FCI obtained during the selection.prepareGetDatais now allowed inside a session for this tag only; if the returned value differs from the Select Application response, theInconsistentDataerror is raised byprocessCommands(not byprepareGetData).
maxDuration comes first, as it is the value the operation assigns.
Measured duration: each bound covers the relevant command exchange alone, from the transmission of the command to the reception of its response; the other commands of the secure session or of the SV operation are not counted. Consequence of an overrun: an open secure session is automatically cancelled and the error is propagated as an
InvalidCardResponse; outside a session, only the error is propagated (see §3.1).
The regular expression on the FCI alone covers the DF name, the startup information (product families, byte masking) and even a prefix of the serial number (tag
C7), with a priority order chosen by the integrator. This form replaces thedfName/startupInfocriteria of the previous working versions of this document.
3.4 Generic Card API
GenericCardTransactionManager.prepareCommandWithMaxDuration(commandId: Int, apdu: ByteArray, maxDuration: Long) → Self— prepares a command with an identifier (see Theme 7) and a duration bound. If the effective duration exceeds the bound, theInvalidCardResponseerror identifies the offending command.
The Generic Card API thus exposes the relay countermeasure at the level of each individual command, consistent with its usage model (APDU sequences without an explicit secure transaction).
3.5 Storage Card API
- New data class
StorageCardSecuritySettings(storagecard.transaction), with the propertyreadCommandMaxDurations: Map<StorageCardProductType, Long> = emptyMap(): maximum duration, in microseconds, of the exchange of a single read command, for each product type. A product type that is absent is not bounded. One instance may be shared by every transaction of a terminal. - Factory operation changed:
createStorageCardTransactionManager(reader, card, securitySettings) → StorageCardTransactionManager. A defaultStorageCardSecuritySettingsdisables every duration bound. - Scope: the bound applies to the read commands prepared on the transaction manager (
prepareReadBlock,prepareReadBlocks,prepareSt25ReadSystemBlock); it applies neither to the selection, nor to the write and authentication commands. An overrun raisesStorageCardInvalidCardResponse, which already carriesblockAddressandcommandId.
Targeted threat: for storage cards, the threat is not the relay but card emulation by a generic RFID device, which does not answer a read command in the same time as the chip of the expected product.
Storage cards have neither an FCI nor a secure session: the product type is enough to segment the fleet, where the Calypso Card API uses the CSN and the FCI.
3.6 Rationale
Both the relay and the emulation introduce a significant and systematic timing deviation on APDU exchanges; monitoring this deviation at the reader level (Card API), on the bounded commands of a Calypso transaction (Calypso Card API), on each generic command (Generic Card API) and on each storage card read (Storage Card API) covers all the usage scenarios of the Terminal APIs.
4. Theme 3 — Simplified observation management
4.1 Motivation
The production model exposed a complete Observer pattern (addObserver, removeObserver, clearObservers, countObservers, setReaderObservationExceptionHandler) plus two distinct SPIs (CardReaderObserverSpi and CardReaderObservationExceptionHandlerSpi). In practice, a single observer is registered, and the separation between event handler and error handler brought no value.
4.2 Reader API — ObservableCardReader
Removed operations:
setReaderObservationExceptionHandler(CardReaderObservationExceptionHandlerSpi)addObserver(CardReaderObserverSpi),removeObserver(CardReaderObserverSpi),clearObservers(),countObservers()startCardDetection(DetectionMode)(single-argument signature)finalizeCardProcessing()(renamed, see below)
Added / redesigned operations:
startCardDetection(settings: CardDetectionSettings, eventHandler: CardReaderEventHandler) → Unit— the handler is registered when detection starts, in a single operation, together with the detection settings (see Theme 6).endCardProcessing() → Unit— replacesfinalizeCardProcessing(); idempotent; additionally releases the references to theSmartCardinstances of the last selection (see §2.3.2).clearScheduledCardSelectionScenario() → Unit— new operation that removes the card selection scenario scheduled on the reader. From the next insertion onwards, no scenario is executed andCardReaderEvent.scheduledCardSelectionsResponseisnull. Idempotent.
Removed SPIs: CardReaderObserverSpi, CardReaderObservationExceptionHandlerSpi.
Added SPI: CardReaderEventHandler, which merges the two previous SPIs:
onReaderEvent(cardReaderEvent: CardReaderEvent) → Unit;onReaderError(context: String, readerName: String, error: Any) → Unit.
4.3 Rationale
This simplification:
- reduces the API surface (5 operations and 2 SPIs become 1 operation and 1 SPI);
- eliminates invalid states (observer registered without an error handler, detection started without an observer, etc.);
- aligns the API with the actual usage observed among integrators.
5. Theme 4 — Knowledge of the current secure session state
5.1 Motivation
On the Calypso side, the application had no direct means of knowing whether a secure session was open, nor its nature (symmetric / asymmetric) or its write access level.
5.2 Calypso Card API
- New operation
TransactionManager.getSecureSessionState() → SecureSessionState— returns the state of the secure session at the moment of the call. - New enumeration
SecureSessionState:NO_SESSION— no secure session is open: none has been opened yet, or the last one has been closed or cancelled;ASYMMETRIC— PKI session;SYMMETRIC_PERSONALIZATION,SYMMETRIC_LOAD,SYMMETRIC_DEBIT— symmetric session opened with the corresponding write access level.
Change compared with the previous working version of this document: the
SecureSessionStatusobject (withisOpen,type,writeAccessLevel) /SecureSessionTypepair is replaced by a single enumeration. The former form lefttypeandwriteAccessLevelundefined when no session was open; the enumeration makes these combinations impossible. The name State (rather than Status) designates one state among mutually exclusive states and avoids any confusion with the status data returned by the card (status word,dfStatus, etc.).
Chosen granularity: the values reflect the cryptographic nature of the session and not the application mode (Regular / Extended); the latter is deduced from the sub-type of the instantiated
TransactionManager.
5.3 Rationale
The Calypso TransactionManager can now be introspected about its own secure session, which spares the caller from maintaining its own state tracking.
6. Theme 5 — Semantic improvements (renamings and removals)
This theme groups the renamings and removals motivated by clarity or consistency. The changes resulting from the language-independent notation (Exception, Spi, Api suffixes, nested enumerations, overloads) are described in Theme 10; the exhaustive list is given in Annex A.
6.1 Reader API
| Before (Java 2.1.0) | After (3.0.0) | Rationale |
|---|---|---|
ObservableCardReader.DetectionMode.SINGLESHOT | DetectionMode.SINGLE_SHOT | UPPER_SNAKE_CASE convention (compound word). |
CardReaderEvent.Type.UNAVAILABLE | CardReaderEventType.READER_UNREGISTERED | The name describes the actual cause of the event. |
ObservableCardReader.NotificationMode | CardPresenceNotificationPolicy (namespace reader.selection) | The name describes what is notified; the notion belongs to selection. |
ObservableCardReader.finalizeCardProcessing() | ObservableCardReader.endCardProcessing() | finalize is loaded in Java (method of Object, deprecated by the JDK). |
Removals:
CardSelectionManager.setMultipleSelectionMode()andprepareReleaseChannel()— see Themes 1 and 9;ChannelControlandCardTransactionManager.processCommands(ChannelControl)— replaced byprocessCommands();ReaderProtocolNotSupportedException— disappears withConfigurableCardReader(see Theme 6);reader.selection.InvalidCardResponseException— duplicate of the error of the same name in thereadernamespace.
6.2 Card API
- Removal of
ChannelControl, ofProxyReaderApi.releaseChannel()and ofCardResponseApi.isLogicalChannelOpen()(see Theme 1). - Removal of the abstract error
AbstractApduException: its information (cardResponse,isCardResponseComplete) is carried directly by the four errors concerned (ReaderBrokenCommunication,CardBrokenCommunication,UnexpectedStatusWord,ApduExchangeDurationExceeded).
6.3 Calypso Card API
- Removals:
TransactionManager.processCommands(ChannelControl)andChannelControl(deprecated);- the errors
UnexpectedCommandStatusException,ReaderIOException,CardIOException(deprecated), covered byInvalidCardResponse,ReaderCommunicationandCardCommunicationof the Reader API; - the
SelectFileExceptionerror, which has become pointless (see Theme 13); CalypsoCardApiFactory.createSearchCommandData()(see Theme 11).
- Renaming of overloaded operations (see Theme 10):
prepareSelectFile(short)→prepareSelectFileByLid,prepareSelectFile(SelectFileControl)→prepareSelectFileByControl(parameter harmonised asselectFileControl), inCalypsoCardSelectionExtensionandTransactionManager. - Nested enumerations renamed:
CalypsoCard.ProductType→CalypsoCardProductType,ElementaryFile.Type→ElementaryFileType. - Collections named in the plural: the
counterNumberToDecValueMap/counterNumberToIncValueMapparameters ofprepareDecreaseCounters/prepareIncreaseCountersbecomedecrementValues/incrementValues; thekif/kvcproperties ofDirectoryHeaderbecomekifByAccessLevel/kvcByAccessLevel. - Security settings renamed to the plural:
SymmetricCryptoSecuritySetting→SymmetricCryptoSecuritySettings,AsymmetricCryptoSecuritySetting→AsymmetricCryptoSecuritySettings; the factory operations follow (createSymmetricCryptoSecuritySettings,createAsymmetricCryptoSecuritySettings), as does thesecuritySettingsparameter of thecreateSecure…TransactionManageroperations.
6.4 Legacy SAM API
- Removals:
TransactionManager.processCommands()(deprecated) andprocessCommands(ChannelControl): the manager now inheritsCardTransactionManager.processCommands()from the Reader API;- the errors
UnexpectedCommandStatusException,ReaderIOException,SamIOException, covered by the Reader API errors; LegacySamRevocationServiceSpi.isSamRevoked(serialNumber)(variant without counter value): onlyisSamRevoked(serialNumber: ByteArray, counterValue: Int) → Booleanremains.
- Renaming of overloaded operations (see Theme 10):
| Before (Java 1.0.0) | After (2.0.0) |
|---|---|
setUnlockData(String, LegacySam.ProductType) | setUnlockDataForProductType(unlockData, productType) |
setStaticUnlockDataProvider(provider) | setStaticUnlockDataProviderWithDeferredReader(provider) |
setStaticUnlockDataProvider(provider, targetSamReader) | setStaticUnlockDataProvider(provider, targetSamReader) (nominal case, name unchanged) |
setDynamicUnlockDataProvider(provider) | setDynamicUnlockDataProviderWithDeferredReader(provider) |
setDynamicUnlockDataProvider(provider, targetSamReader) | setDynamicUnlockDataProvider(provider, targetSamReader) (nominal case, name unchanged) |
prepareReadWorkKeyParameters(int) / (byte, byte) | prepareReadWorkKeyParametersByRecordNumber / prepareReadWorkKeyParametersByKifKvc |
getWorkKeyParameter(int) / (byte, byte) | getWorkKeyParametersByRecordNumber / getWorkKeyParametersByKifKvc |
prepareTransferWorkKeyDiversified(…, diversifier) | prepareTransferWorkKeyDiversifiedWithSpecificDiversifier(…, diversifier) |
LegacySam.ProductType | LegacySamProductType |
6.5 Generic Card API
| Before (Java 1.0.0) | After (2.0.0) | Rationale |
|---|---|---|
CardTransactionManager | GenericCardTransactionManager | Name specific to the API, with no collision with the Reader API CardTransactionManager it inherits from. |
GenericCardApiFactory.createCardTransaction(reader, card) | createGenericCardTransactionManager(reader, card) | The name designates the created object. |
prepareApdu(String) | (removed) | Conversion from a string is left to the application. |
prepareApdu(byte[]) | prepareCommand(apdu: ByteArray) | “Command”-oriented name. |
prepareApdu(byte cla, byte ins, byte p1, byte p2, byte[] dataIn, Byte le) | (removed) | Building APDUs field by field is left to the application. |
getResponsesAsByteArrays() | getLastExecutionResponses() → List<ByteArray> | Specifies the time scope (last execution). |
getResponsesAsHexStrings() | (removed) | The hexadecimal representation is left to the application. |
New operations related to Themes 2 and 7: prepareCommandWithId, prepareCommandWithMaxDuration, getLastExecutionResponse (see §8.2).
6.6 Storage Card API
| Before (Java 1.2.0) | After (2.0.0) | Rationale |
|---|---|---|
ProductType (namespace storagecard.card) | StorageCardProductType | Name specific to the API, unambiguous with respect to the other product types of the family. |
ProductType.getBlockCount(), getBlockSize(), hasSystemBlock(), hasWriteAcknowledgment(), hasAuthentication() | properties blockCount, blockSize, hasSystemBlock, hasWriteAcknowledgment, hasAuthentication of the enumeration | These are data specific to each product, exposed as such (see Theme 10). |
StorageCard.getUID() | StorageCard.getUid() | lowerCamelCase convention for acronyms. |
prepareMifareClassicAuthenticate(…, byte[] key) | prepareMifareClassicAuthenticateWithKey(…, key) | Unique operation names (see Theme 10). |
prepareMifareClassicAuthenticate(…, int keyNumber) | prepareMifareClassicAuthenticateWithKeyNumber(…, keyNumber) | (same) |
StorageCardTransactionManager.prepareReadSystemBlock(), prepareWriteSystemBlock(byte[]) (deprecated) | (removed); prepareSt25ReadSystemBlock() and prepareSt25WriteSystemBlock(commandId, data) remain | The St25 prefix reflects the product-specific nature of the system block. |
StorageCardException interface (getBlockAddress()) | (removed); the errors carry blockAddress: Int? and commandId: Int? | The information is carried directly by each error. |
SCAuthenticationFailedException extends CardCommunicationException | StorageCardAuthenticationFailed (no parent error) | An authentication failure is not a communication error. |
In addition, StorageCard.getBlock, getBlocks and getSystemBlock now explicitly return ByteArray? (null if the data has not been read).
6.7 Crypto Symmetric and Crypto Asymmetric APIs
The evolutions of these two APIs relate to Themes 10 and 11 (data objects, removal of input/output objects); they are detailed in §11 and §12 and in Annex A.
7. Theme 6 — Strict typing of RF technologies and card types (ECP support)
7.1 Motivation
Two drivers converge:
- End of free-form strings for protocols. The production versions configured protocol activation and selection filtering with character strings (
physicalProtocolName,logicalProtocolName): non-standardised values, undetected typos, scattered documentation. - Arrival of ECP support (Enhanced Contactless Polling), a mechanism defined by the Apple ECP specification that allows fast detection of cards (notably Calypso) in transit mode on iPhone, and requires sending a specific polling frame when detection starts. The frame is handled as opaque binary data built by the application.
7.2 New foundation API — Terminal Reader Definitions API
The RfTechnology and CardType enumerations are placed in a new foundation API, the Terminal Reader Definitions API, which exposes neither service interface nor SPI; its only purpose is to host the cross-cutting enumerated types shared between Terminal APIs.
Structural consequences
- New repository:
calypsonet-terminal-reader-definitions-uml-api(version1.0.0-SNAPSHOT). - New Keypop Java module:
keypop-reader-definitions-jvm-api(to be created). - Public and transitive dependency of the Terminal Reader API on the Terminal Reader Definitions API (the enumerations appear in the properties
BasicCardSelector.cardType,IsoCardSelector.cardType,CardDetectionSettings.rfTechnologiesand…CardSelectionResult.cardType).
Initial content
ReaderDefinitionsApiProperties—VERSIONconstant of the module;RfTechnology:ISO_14443_AB,INNOVATRON_B_PRIME,FELICA,ISO_15693;CardType:ISO_7816_3,ISO_14443_4,ISO_14443_3A_MIFARE_CLASSIC_1K,ISO_14443_3A_MIFARE_CLASSIC_4K,ISO_14443_3A_MIFARE_ULTRALIGHT,ISO_14443_3B_ST25_SRT512,INNOVATRON_B_PRIME,FELICA,ISO_15693,UNKNOWN.
CardTypegranularity and A/B asymmetry withRfTechnology:RfTechnologyis an input (polling) that merges A and B at the ISO 14443 level;CardTypeis an output that combines protocol level and product identity — a singleISO_14443_4value for the transport, but a product granularity (ISO_14443_3A_…,ISO_14443_3B_…) for proprietary products.
CardType.UNKNOWNis returned when the card type could not be identified; used as a selection filter, it allows these cards to be captured explicitly.
7.3 Reader API — strict typing
- Removed:
CardSelector.filterByCardProtocol(String logicalProtocolName). - Added: the
cardType: CardType? = nullproperty of theBasicCardSelectorandIsoCardSelectorselectors — filters by card type (nulldisables the filter). - Removed entirely: the
ConfigurableCardReaderinterface and its operationsactivateProtocol(String, String),deactivateProtocol(String),getCurrentProtocol().
7.4 Reader API — detection settings
All detection information is grouped in the data class CardDetectionSettings, built directly by the application:
| Property | Type | Default value | Role |
|---|---|---|---|
detectionMode | DetectionMode | DetectionMode.REPEATING | whether detection resumes after each card processing |
rfTechnologies | Set<RfTechnology> | setOf(RfTechnology.ISO_14443_AB) | RF technologies activated during polling (no effect on a contact reader) |
ecpFrame | ByteArray? | null | ECP frame emitted when polling starts (ECP readers only) |
DetectionModebecomes a top-level enumeration of thereadernamespace (it was nested inObservableCardReader).- Tolerance of unsupported settings: a setting not supported by the reader (RF technology, ECP frame) is silently ignored and a
WARN-level message is typically logged; no error is raised.
Change compared with the previous working version of this document:
CardDetectionSettingsis no longer a “builder” interface obtained throughReaderApiFactory.createCardDetectionSettings(), but a data class with default values (see Theme 10).
7.5 Reader API — ObservableCardReader and selection result
startCardDetection(settings: CardDetectionSettings, eventHandler: CardReaderEventHandler) → Unitcarries the event handler and the detection configuration in a single call.- The detected card type is exposed by the
cardType: CardTypeproperty of the selection results (SingleCardSelectionResult,SequentialCardSelectionResult,MultichannelCardSelectionResult, see Theme 9);UNKNOWNif the type could not be identified.
7.6 Rationale
- Compile-time safety for values: moving from strings to enumerations eliminates a class of bugs and makes the API self-describing.
- Simplicity: a single
ObservableCardReader, a single detection settings class. - Extensibility: adding a polling setting amounts to adding a property with a default value to
CardDetectionSettings, without touching thestartCardDetectionsignature. - Input / output separation:
RfTechnologyas input (polling),CardTypeas output (result) and as a selection criterion. - Single, typed declaration: in production, the protocol had to be declared twice (
activateProtocolon the reader side,filterByCardProtocolon the selector side); it is now declared only once, in a typed way. - Cross-cutting reusability thanks to the extraction of the enumerations into the Terminal Reader Definitions API.
8. Theme 7 — Command identification (commandId)
8.1 Motivation
Several APIs allow several commands to be prepared before executing them as a batch. In production, the application had no direct means of identifying which command caused a problem, nor of accessing the result of a specific command; some APIs relied on mutable container objects for this purpose (KeyPairContainer, SearchCommandData.getMatchingRecordNumbers(), SignatureComputationData.getSignature(), etc.).
The new versions generalise a single mechanism: an integer identifier commandId supplied by the application when preparing the command, then used to retrieve the result of the command or to identify the failing command. The commandId parameter, always placed first, follows the same convention as selectionCaseId in the Reader API (see Theme 9).
The name
commandIdreplacesidCommandfrom the previous working version of this document, for consistency withselectionCaseId.
8.2 Generic Card API
prepareCommandWithId(commandId: Int, apdu: ByteArray) → Self;prepareCommandWithMaxDuration(commandId: Int, apdu: ByteArray, maxDuration: Long) → Self(see Theme 2);getLastExecutionResponse(commandId: Int) → ByteArray?— response of the identified command; if several commands share the same identifier, the most recently processed one is returned.prepareCommand(apdu: ByteArray) → Selfremains available for unidentified commands.
8.3 Storage Card API
prepareWriteBlocks(commandId: Int, fromBlockAddress: Int, data: ByteArray) → Self;prepareSt25WriteSystemBlock(commandId: Int, data: ByteArray) → Self.
For these two write operations, commandId is mandatory (non-nullable). It is returned by the commandId: Int? property of the Storage Card API errors when the identified command caused them (null for commands that carry none, such as reads).
8.4 Calypso Card API
prepareSearchRecords(commandId: Int, data: SearchCommandData) → Self;CalypsoCard.getMatchingRecordNumbers(commandId: Int) → List<Int>?— numbers of the records found by the identified search. This operation replacesSearchCommandData.getMatchingRecordNumbers()(see Theme 11).
8.5 Legacy SAM API
The results of the cryptographic commands are no longer read from container objects but from the LegacySam, by identifier:
| Preparation | Reading the result |
|---|---|
FreeTransactionManager.prepareGenerateCardAsymmetricKeyPair(commandId: Int) | LegacySam.getKeyPair(commandId: Int) → ByteArray? |
FreeTransactionManager.prepareComputeCardCertificate(commandId: Int, data: LegacyCardCertificateComputationData) | LegacySam.getComputedCardCertificate(commandId: Int) → ByteArray? |
prepareComputeSignature(commandId: Int, data: SignatureComputationData) (on FreeTransactionManager and CardTransactionLegacySamExtension) | LegacySam.getSignature(commandId: Int) → ByteArray? and, in traceable mode, LegacySam.getSignedData(commandId: Int) → ByteArray? |
prepareVerifySignature(commandId: Int, data: SignatureVerificationData) (same) | LegacySam.isSignatureValid(commandId: Int) → Boolean? |
These reads replace KeyPairContainer.getKeyPair(), LegacyCardCertificateComputationData.getCertificate(), SignatureComputationData.getSignature(), TraceableSignatureComputationData.getSignedData() and SignatureVerificationData.isSignatureValid().
8.6 Rationale
- Application semantics: the identifier is chosen by the application, which can align it with its own business logic.
- Immutable data objects: the parameters of a command become pure data (see Theme 10); the result is carried by the live object that receives the responses (
CalypsoCard,LegacySam, transaction manager), like all the other data coming from the card. - Cross-API consistency: the same pattern (identifier first, read by identifier) applies to all the APIs concerned.
9. Theme 8 — Standardised reader discovery and access (CardReaderProvider)
9.1 Motivation
In production, the Reader API exposed no standard means of discovering the available readers or of obtaining a reference to a reader. The application had to rely on the plugin and pool abstractions of the underlying Keyple framework, which are implementation-specific and not portable.
9.2 Reader API
- New operation
ReaderApiFactory.getCardReaderProvider() → CardReaderProvider— returns theCardReaderProviderof the execution environment; successive calls return the same instance. - New interface
CardReaderProvider:getReaderNames() → Set<String>;getReaders() → Set<CardReader>;getReader(readerName: String) → CardReader?— exact name match,nullif no reader matches;findReader(readerNameRegex: String) → CardReader?— first reader whose name matches the regular expression,nullotherwise.
The reader lifecycle remains driven by the execution environment; CardReaderProvider is a read-only view of the readers active at each call.
9.3 Rationale
- Decoupling from Keyple implementation concepts: the application only knows the Reader API.
- First-class citizen of the public API for a universal need.
- Two complementary search modes: exact name and regular expression.
10. Theme 9 — Redesign of the card selection model
10.1 Motivation
In production, the CardSelectionManager gathered all selection modes, driven by a side effect (setMultipleSelectionMode()), and returned a single CardSelectionResult in which some properties could be deduced from others (getActiveSmartCard() and getActiveSelectionIndex() from getSmartCards()). Adding multi-channel made this single result ambiguous (which card is “the” active card when several are active?). The previous working version of this document proposed a SelectionExecutionPolicy parameter; this approach has been abandoned in favour of one manager type per selection mode.
10.2 Reader API — one manager per mode
The selection mode is no longer a parameter: it is carried by the type of the manager, each obtained through its own factory operation and producing its own result type.
| Manager | Behaviour | Execution | Result |
|---|---|---|---|
SingleCardSelectionManager | single-channel; stops at the first matching case: the first card application, or the card itself when it hosts no application (storage card) | explicit or scheduled | SingleCardSelectionResult |
SequentialCardSelectionManager | single-channel; selects in turn every matching application of the card, only the last one remaining active; not applicable to storage cards | explicit or scheduled | SequentialCardSelectionResult |
MultichannelCardSelectionManager | multi-channel ISO 7816-4 cards; every matching application stays active simultaneously, each on its own logical channel; out of scope for storage cards | explicit | MultichannelCardSelectionResult |
TC remark (Stéphane): the scenarios discriminate the applications of a single physical card; the API keeps the term card because a selection case also targets cards without any application, such as storage cards. This is now made explicit in the specification, in the
CardSelectionManagersection.
Compared with the previous working version of this document,
MultipleCardSelectionManageris renamedSequentialCardSelectionManager(as are its result and its factory operation): the qualifier describes the scenario execution mode, not a number of cards. Likewise,prepareSelectionbecomesprepareSelectionCaseandselectionIdbecomesselectionCaseId.
- Factory:
createCardSelectionManager()is replaced bycreateSingleCardSelectionManager(),createSequentialCardSelectionManager()andcreateMultichannelCardSelectionManager(). CardSelectionManagerbecomes the common interface and only keeps the mode-independent operations:prepareSelectionCase(selectionCaseId: Int, cardSelector: CardSelector, cardSelectionExtension: CardSelectionExtension) → Self— the selection identifier is chosen by the application (instead of an index returned by the API); it must be unique within the scenario; selections are executed in preparation order;exportCardSelectionScenario() → String;importCardSelectionScenario(cardSelectionScenario: String) → Self— replaces the current scenario (instead of returning the index of the last imported selection);exportProcessedCardSelectionScenario() → String.
- Operations specific to each manager (typed by their result):
processCardSelectionScenario,scheduleCardSelectionScenarioandparseScheduledCardSelectionsResponse(single-channel only),importProcessedCardSelectionScenario(the imported processed scenario must come from a manager of the same type). scheduleCardSelectionScenario(observableCardReader: ObservableCardReader, cardPresenceNotificationPolicy: CardPresenceNotificationPolicy) → Unitno longer has an execution policy parameter.
Selection results
| Result | Properties |
|---|---|
SingleCardSelectionResult | cardType: CardType, selectionCaseId: Int?, smartCard: SmartCard? (both null if no selection succeeded) |
SequentialCardSelectionResult | cardType: CardType, smartCards: Map<Int, SmartCard>, activeSelectionCaseId: Int? (only the card of the last successful selection remains active) |
MultichannelCardSelectionResult | cardType: CardType, smartCards: Map<Int, SmartCard> (all active, one per channel) |
CardSelectionResult (with getSmartCards(), getActiveSmartCard(), getActiveSelectionIndex()) and SelectionExecutionPolicy disappear.
Selectors
BasicCardSelectorandIsoCardSelectorbecome data classes built directly by the application (instead of “builder” interfaces created byReaderApiFactory.createBasicCardSelector()/createIsoCardSelector(), which are removed):BasicCardSelector:cardType: CardType? = null,powerOnDataRegex: String? = null;IsoCardSelector: the same, plusdfName: ByteArray? = null,fileOccurrence: FileOccurrence = FileOccurrence.FIRST,fileControlInformation: FileControlInformation = FileControlInformation.FCI.
CardSelector<T>becomes a sealed interface without members, whose only implementations are these two selectors.- The intermediate interface
CommonIsoCardSelector<T>is removed; its nested enumerations becomeFileOccurrenceandFileControlInformation(namespacereader.selection). filterByDfName(String)(AID in hexadecimal) is removed:dfNameis aByteArray.
10.3 Rationale
- No side effect: the selection mode is set when the manager is created.
- Unambiguous results: each result only exposes what makes sense in its mode; deducible properties and the “channel 0 card” / “first active index” contradiction disappear.
- Identifiers chosen by the application, consistent with
commandId(Theme 7). - Naming: Single and Multiple designate the number of successful selections kept in the result, not a number of cards; all the selections of a scenario target the same card.
11. Theme 10 — Implementation-language-independent specification
11.1 Motivation
The Terminal APIs were until now defined by Java interfaces. This model de facto tied the APIs to the JVM and hindered their implementation in other environments (native mobile applications, embedded systems, non-Java terminals). The new design proposal aims to broaden the choice of implementation languages: Kotlin Multiplatform (KMP), Rust, Swift, C#, etc., in addition to Java. The new specifications are therefore written in a language-independent notation, inspired by Kotlin, so that each binding translates the contract into the most idiomatic form of its platform. This notation entails systematic changes of form, described below; most of them do not affect behaviour, but all of them affect the way calling code is written.
11.2 Notation principles
| Principle | In production (Java) | In the specifications | Examples |
|---|---|---|---|
| Basic types | byte, short, int, long, boolean, byte[], Integer… | Byte, Short, Int, Long, Boolean, ByteArray, Int?… (Data types table of each spec) | — |
| Explicit nullability | null values documented in the Javadoc | T? type; otherwise the value is never null | getFileBySfi(sfi: Byte) → ElementaryFile? |
| Fluent chaining | recursive genericity T extends X<T> | Self return type; no more recursive genericity | TransactionManager.prepareReadRecords(...) → Self |
| Data classes | “builder” interfaces (setters) or read interfaces (getters), created by the factory | immutable data classes with properties (val) and default values, built directly by the application; the corresponding create… operations disappear from the factory | SearchCommandData, BasicCardSelector, CardDetectionSettings, ApduRequest |
| Enumerations | enumerations nested in an interface | top-level enumerations, named autonomously | CardReaderEvent.Type → CardReaderEventType |
| Data specific to an enumeration value | methods of the enumeration | properties of the enumeration | StorageCardProductType.blockSize |
| Closed types | open generic interface | sealed interface | CardSelector, SignatureComputationData, SignatureVerificationData |
| Interfaces without operations | empty interface | marker interface | ScheduledCardSelectionsResponse, CardSelectionExtension |
| Errors | …Exception classes (checked or unchecked) | errors named without suffix, carrying message: String and cause: Any? | CardCommunicationException → CardCommunication |
| Technical suffixes | …Spi, …Api on the data types of the Card API and the crypto APIs | removed for data; kept for contract interfaces | ApduRequestSpi → ApduRequest, SvCommandSecurityDataApi → SvCommandSecurityData |
| Unique operation names | overloads (same name, different parameters) | one unique name per operation within an interface and its hierarchy; By…, With…, For… suffixes | prepareSelectFile → prepareSelectFileByLid / prepareSelectFileByControl |
| Universal type | Object, Throwable | Any | onReaderError(context, readerName, error: Any) |
| Reflection | Class<E> | removed | getCryptoExtension(Class<E>) → getCryptoExtension() (see Theme 14) |
| Serialisation | extends Serializable | removed from the notation | ApduResponseApi, CardResponseApi |
11.3 Normative contracts
Each operation is now described by a normative table that specifies:
- its signature, its introduction version (Since) and its description;
- its pre-conditions, each prefixed by its nature, which determines the error raised if it is not met: Argument (invalid argument, including a value outside the bounds of the card or SAM protocol), Range (position outside the bounds of a collection or memory image exposed by the API), State (illegal state), Capability (unsupported operation);
- its errors, i.e. the situations a correct caller must handle;
- cross-references to related operations and types.
The specifications also define common rules (non-null results unless stated otherwise, empty collections rather than null, instances not shared between threads, default parameter values) as well as, for the Card API and the crypto APIs, conformance clauses that establish the correspondence between their types and those of the Reader API or the Calypso Card API (for example, any object implementing ProxyReaderApi must also implement CardReader).
11.4 Rationale
- Portability: the contract no longer imposes any Java-specific mechanism (reflection, recursive genericity, overloads); it can be transposed into any language, including those without overloading or class inheritance (Rust in particular), which paves the way for implementations in Kotlin Multiplatform, Rust, Swift or C#.
- Robustness: explicit nullability and immutable data eliminate invalid states (incomplete “builder” object, unexpected
nullvalue). - Precision: typed pre-conditions and errors listed per operation make the contract verifiable.
- Java binding: the way the Java binding will implement these principles (data classes,
Self, default values) is part of the alignment of the Keypop modules (see §19.3) and of the migration guide.
12. Theme 11 — Data exposed without computation and access to raw data
12.1 Motivation
Several data types of the production versions mixed data and computations (decoding a counter from a record, selecting a parameter by number), exposed both a raw value and its decoded fields, or served both as input and output. The specifications apply three rules:
- no computational operation on a data type: such computations are performed by the live object that holds the data (
CalypsoCard,LegacySam); - no field deducible from other fields in a data class;
- no input/output object: inputs are parameters, outputs are returned values.
12.2 Calypso Card API
FileDatais removed, together with its operationsgetContent(),getContent(numRecord),getContent(numRecord, dataOffset, dataLength),getAllRecordsContent(),getContentAsCounterValue(numCounter)andgetAllCountersValue():- the records are exposed directly by the
ElementaryFile.records: SortedMap<Int, ByteArray>property (instead ofElementaryFile.getData()); - counter values are obtained through
CalypsoCard.getCounterValuesBySfi(sfi: Byte) → SortedMap<Int, Int>?andCalypsoCard.getCounterValuesByLid(lid: Short) → SortedMap<Int, Int>?.
- the records are exposed directly by the
DirectoryHeader:getKif(WriteAccessLevel)andgetKvc(WriteAccessLevel)become the propertieskifByAccessLevel: Map<WriteAccessLevel, Byte>andkvcByAccessLevel: Map<WriteAccessLevel, Byte>.SearchCommandDatabecomes an input data class (sfi,searchData,startAtRecord = 1,offset = 0,repeatedOffset = false,mask: ByteArray? = null,fetchFirstMatchingResult = false); the result is read throughCalypsoCard.getMatchingRecordNumbers(commandId)(see Theme 7);CalypsoCardApiFactory.createSearchCommandData()disappears.SvLoadLogRecordandSvDebitLogRecordbecome data classes without therawDataproperty, which is redundant with the decoded fields. The raw values remain accessible through three newCalypsoCardoperations:getSvLoadLogRecordRawData() → ByteArray?,getSvDebitLogLastRecordRawData() → ByteArray?andgetSvDebitLogAllRecordsRawData() → List<ByteArray>; each returned decoded object is the decoding of the raw value at the moment of the call.DirectoryHeader,ElementaryFileandFileHeaderbecome data classes.
12.3 Legacy SAM API
KeyParameterbecomes theKeyParametersdata class, renamed to the plural like thegetSystemKeyParameters/getWorkKeyParameters…getters that return it (kif,kvc,algorithm,parameterValues: SortedMap<Int, Byte>), withoutrawData;getParameterValue(parameterNumber)is replaced by theparameterValuesproperty. The raw values are accessible throughLegacySam.getSystemKeyParametersRawData(systemKeyType),getWorkKeyParametersRawDataByRecordNumber(recordNumber)andgetWorkKeyParametersRawDataByKifKvc(kif, kvc).SamParametersis removed:LegacySam.getSamParameters()directly returnsByteArray?.- Counters:
getCounter(counterNumber)andgetCounterCeiling(counterNumber)are removed (thegetCounters()andgetCounterCeilings()tables are sufficient);getCounterIncrementAccess(counterNumber)is replaced bygetCounterIncrementAccesses() → SortedMap<Int, CounterIncrementAccess>. - Command data:
LegacyCardCertificateComputationData,BasicSignatureComputationData,TraceableSignatureComputationData,BasicSignatureVerificationDataandTraceableSignatureVerificationDatabecome input data classes (properties and default values instead of setters;withSamTraceabilityMode(offset, mode)becomessamTraceabilityMode/traceabilityOffset,withoutBusyMode()becomesbusyMode = false); their results are read from theLegacySambycommandId(see §8.5).KeyPairContaineris removed. Thecreate…Data()andcreateKeyPairContainer()operations disappear fromLegacySamApiFactory. SecuritySettingbecomes theLegacySamSecuritySettingsdata class (samReader,controlSam), renamed to the plural like the security settings of the Calypso Card API, and prefixed to avoid the homonymy with theSecuritySettingsof the Calypso Card API, instead ofsetControlSamResource(samReader, controlSam);LegacySamApiFactory.createSecuritySetting()disappears.
12.4 Card API
ApduResponseonly keepsapdu(andapduExchangeDuration):getDataOut()andgetStatusWord()are removed, as they can be deduced fromapdu.ApduRequestSpi,CardRequestSpi,CardSelectionRequestSpi,ApduResponseApi,CardResponseApiandCardSelectionResponseApibecome the data classesApduRequest,CardRequest,CardSelectionRequest,ApduResponse,CardResponseandCardSelectionResponse. Default values are explicit:successfulStatusWords = setOf(0x9000),successfulSelectionStatusWords = setOf(0x9000),info = null,cardRequest = null.
12.5 Crypto Symmetric API
SvCommandSecurityDataApi(input/output object) is replaced:- the inputs become parameters:
computeSvCommandSecurityData(svGetRequest: ByteArray, svGetResponse: ByteArray, svCommandPartialRequest: ByteArray) → SvCommandSecurityData; - the output is the
SvCommandSecurityDatadata class (serialNumber,transactionNumber,terminalChallenge,terminalSvMac, non-nullable).
- the inputs become parameters:
createCardTransactionManager(..., transactionAuditData: MutableList<ByteArray>): the audit list is explicitly mutable (the crypto module appends its data to it).cipherPinForPresentationandcipherPinForModificationtake non-nullablekif: Byteandkvc: Byte(instead of boxedByte).
12.6 Crypto Asymmetric API
CaCertificateContentSpibecomes theCaCertificateContentdata class; theisAidCheckRequestedproperty is removed, as it can be deduced fromaid(nullwhen the AID check is not requested).CardPublicKeySpiis removed: the card public key is aByteArray(CardCertificateSpi.checkCertificateAndGetPublicKey(...) → ByteArray,AsymmetricCryptoCardTransactionManagerSpi.initTerminalPkiSession(cardPublicKey: ByteArray)).- A conformance clause establishes the correspondence between the SPIs of this API and the marker interfaces of the Calypso Card API (
PcaCertificate,CaCertificate,CardCertificate, the parsers and the factory).
12.7 Rationale
- An API describes data and behaviours, not decoding algorithms: computations on data remain the responsibility of the object that holds them.
- No double truth: a deducible field can diverge from its source; removing it eliminates the risk.
- Raw access preserved where it has a real use (transmission to the back office, re-injection of key parameters into transfer commands).
13. Theme 12 — Stored Value (SV) operations
13.1 Motivation
The Calypso card specification defines three SV commands — Reload, Debit and Undebit — preceded by an SV Get command, one parameter of which indicates the targeted operation: Reload or Debit/Undebit. The production model additionally introduced a DO/UNDO notion (SvAction) that does not exist for reloading and hid the undebit behind prepareSvDebit.
13.2 Calypso Card API
SvActionis removed.prepareSvGet(svOperation: SvOperation) → Self— only takes the operation.SvOperation.DEBITis renamedDEBIT_UNDEBIT, in accordance with the card specification;RELOADis unchanged.- New operation
prepareSvUndebit(amount: Int, date: ByteArray, time: ByteArray) → Self— cancels, totally or partially, a previous debit; amount in0..32768. prepareSvDebit(amount: Int, date: ByteArray, time: ByteArray) → Self— now only performs the debit; amount in0..32767.prepareSvReload(amount: Int, date: ByteArray, time: ByteArray, free: ByteArray) → Self— amount in-8388608..8388607(a negative reload is expressed directly by a negative amount).- The overloads without data
prepareSvDebit(int)andprepareSvReload(int)are removed: all parameters are mandatory and non-nullable; thedate,timeandfreefields (2 bytes each) are recorded in the SV log. - Each command requires a prior SV Get prepared with the corresponding operation (
DEBIT_UNDEBITfor debit and undebit,RELOADfor reload).
13.3 Rationale
The model now follows the card specification exactly: three commands, two SV Get contexts. The amount range and the log data are explicit in each operation.
14. Theme 13 — Tolerance of a missing file or record in a secure session
14.1 Motivation
All Calypso cards now tolerate the 6A82h (File Not Found) and 6A83h (Record Not Found) status words in a secure session for read commands (Select File, Get Data, Read Binary, Read Records, Read Record Multiple, Search Record Multiple). On a heterogeneous card fleet, the presence of a file or of a record is not always known in advance, and an unsuccessful read should not cancel the session.
The tolerance is explicitly enabled by the integrator: the default behaviour, stricter, remains the failure of the transaction inside a session.
14.2 Calypso Card API
- Two new security settings:
authorizeFileNotFoundError() → SelfandauthorizeRecordNotFoundError() → Self, carried by the parent interfaceSecuritySettingsand therefore available inSymmetricCryptoSecuritySettingsas well as inAsymmetricCryptoSecuritySettings. They allow the card to answer6A82hor6A83hinside a session without failing the transaction: the affected command is simply not applied to theCalypsoCardand the session continues. They are disabled by default. - Reads (
prepareReadBinary,prepareReadCounter,prepareReadRecords) and file selection (prepareSelectFileByLid,prepareSelectFileByControl): outside a session, a missing file has never caused processing to fail, and this best-effort mode is unchanged; inside a session, processing fails unless the corresponding setting has been enabled. An invalid offset keeps the two modes best-effort (outside a session) and strict (inside a session). - The
SelectFileExceptionerror is removed. - The in-session usage restrictions of
prepareGetData,prepareReadRecord,prepareReadRecordsPartiallyandprepareSearchRecordsare unchanged.
15. Theme 14 — Crypto extensions and command interleaving
15.1 Motivation
During a card transaction, the application must be able to access the specific operations of the crypto module (for example signature computation by a SAM) in order to interleave card commands and crypto commands within the same transaction. The crypto extension is the instance created, for the current transaction, by the crypto module factory attached to the security setting.
15.2 Calypso Card API
<E extends CardTransactionCryptoExtension> E getCryptoExtension(Class<E> cryptoExtensionClass)becomesgetCryptoExtension() → CardTransactionCryptoExtension:- the operation returns the instance created for this transaction; successive calls return the same instance;
- commands prepared through the extension join the same queue as the card commands, in call order, and are processed by the same
processCommands(); - the caller converts the instance to the concrete type defined by the crypto module in use (for example
CardTransactionLegacySamExtension).
15.3 Legacy SAM API
CardTransactionLegacySamExtension.prepareComputeSignature(commandId, data)andprepareVerifySignature(commandId, data)follow thecommandIdmodel (see §8.5).
15.4 Rationale
The Class<E> parameter only existed to work around the JVM’s type erasure; it has no equivalent in other languages (Rust in particular). The chosen signature is identical in all bindings, and the identity clause guarantees that the obtained extension is indeed the one sharing the transaction’s command queue.
16. Normative clarifications
The specifications also bring clarifications that do not change signatures but specify the contract:
- Card API — APDU construction rules: commands must strictly comply with ISO/IEC 7816-3; a case 4 command must include the
Lefield, for which the value00his recommended (it was previously presented as mandatory). - Card API — limitations: the transmission of the Select Application by DF name command (reserved to the
CardSelectionRequest) and of the Get Response command (status words61XYhand6CXYhare handled automatically by the reader implementation) cannot be requested. - Card API — timing countermeasures: the APDU exchange execution-time control mechanism is explicitly presented as a countermeasure against both relay and emulation (see Theme 2).
- Reader API —
SmartCardlifecycle: now normative (see §2.3.2). - All APIs — pre-condition natures: an explicit criterion distinguishes Range (position in a collection or memory image exposed by the API) from Argument (any other invalid value, including values bounded by the card or SAM protocol).
- Storage Card API — scope: the Scope section explicitly lists the supported products (MIFARE Ultralight, MIFARE Classic 1K, MIFARE Classic 4K, ST25 SRT512), identified by the values of
StorageCardProductType.
17. Elements under study
The following elements appear in grey in the Legacy SAM API diagram; they are not part of the normative scope submitted for validation:
LegacySamApiFactory.createSecureReadTransactionManager(samReader, sam, securitySettings)and theSecureReadTransactionManagerinterface;FreeTransactionManager.preparePlainLoadWorkKey(...)andpreparePlainExportWorkKey(...);LegacySamSelectionExtension.prepareReadCaadRecord(...)/prepareReadCaadRecords(...)and their equivalents onReadTransactionManager;SecureWriteTransactionManager.prepareWriteCaadRecord(...).
18. Migration procedure
Migrating application code from the production versions to the new versions will be covered by a dedicated technical migration guide, published separately after validation by the TC Terminal and after alignment of the associated Keypop Java implementations.
This guide will aim to simplify the transition as much as possible: 1:1 mapping of removed / renamed / redesigned elements (of which Annex A is the basis), rewriting patterns (before / after) for the most common use cases, progressive adoption rules and known pitfalls. It will also describe the Java implementation of the notation principles of Theme 10 (data classes, Self, default values, nullability).
19. Next steps and validation by the TC Terminal
19.1 Scope submitted for validation
This document submits to the validation of the CNA TC Terminal:
- The principle of the fourteen evolution themes (§2 to §15) and the overall consistency of the work (versions 3.0.0 for Reader / Card / Calypso Card, 1.0.0 for Definitions, 2.0.0 for Legacy SAM / Generic Card / Storage Card, 0.2.0 for Crypto Symmetric, 0.3.0 for Crypto Asymmetric).
- The design choices documented in the “Rationale” sections, in particular:
- the explicit multi-channel model relying on the
SmartCard(Spi)as the named target and the three-level hierarchy of transaction managers (§2); - duration bounding at the APDU, Calypso session, generic command and storage card read levels, with CSN-based and FCI-based settings (§3);
- the merge of the Observer pattern into a single
CardReaderEventHandlerSPI (§4); - the
SecureSessionStateenumeration (§5); - the extraction of
RfTechnologyandCardTypeinto the Terminal Reader Definitions API and theCardDetectionSettingsdetection settings (§7); - the generalised
commandIdmodel (§8); - standardised reader discovery through
CardReaderProvider(§9); - the hierarchy of selection managers per mode and the three result types (§10);
- the language-independent notation and its principles, which aim to broaden the choice of implementation languages (KMP, Rust, Swift, etc.) (§11);
- data without computation and access to raw data (§12);
- the SV model aligned with the card specification (§13);
- the tolerance of a missing file in a session (§14);
- access to the crypto extension with an identity clause (§15).
- The detailed content of the nine specifications and their diagrams (see Reference documents).
- The introduction of the new Reader Definitions foundation API.
- The principle of a dedicated migration procedure (see §18).
19.2 Points of attention for the review
- the stability of the initial content of the
RfTechnologyandCardTypeenumerations (§7.2), in particular the representation of ISO 14443-4 by a singleISO_14443_4value; - the complete removal of
ConfigurableCardReaderwithout a deprecation phase (§7.3); - the Calypso duration bound resolution rule: priority of CSN-based over FCI-based settings,
Long.MAX_VALUEas the deferral value, portable subset of regular expressions (§3.3); - the enforcement of the Calypso duration bounds (§3.1, §3.3): both what is measured (the relevant command exchange alone) and the consequence of an overrun (automatic cancellation of an open session, error propagation outside a session) are now specified;
- the three-level gradation of transaction managers (§2.2);
- the replacement of overloads by unique operation names (§11.2), which changes many operation names for Java integrators;
- the replacement of “builder” interfaces by data classes (§11.2, §12), whose Java implementation remains to be defined;
- the change from
SvOperation.DEBITtoDEBIT_UNDEBITand the removal of the SV overloads without data (§13).
19.3 Next steps
Once the versions have been validated by the TC Terminal:
- Finalisation of the specifications: moving the repositories from their
…-SNAPSHOTversions to their final versions; removal or specification of the elements under study (§17). - Creation of the new Java module
keypop-reader-definitions-jvm-api, and alignment of the existing Keypop Java modules (keypop-reader-java-api,keypop-card-java-api,keypop-calypso-card-java-api,keypop-calypso-crypto-legacysam-java-api,keypop-calypso-crypto-symmetric-java-api,keypop-calypso-crypto-asymmetric-java-api,keypop-genericcard-jvm-api,keypop-storagecard-java-api) with their new versions. - Writing and publication of the technical migration guide (see §18).
- Communication of the availability of the new versions to integrators and to the CNA working groups concerned.
Annex A — Detailed mapping per API
This annex lists, for each API, what becomes of each element of the Java versions in production. Unchanged elements (apart from the move to the Theme 10 notation: basic types, Self, explicit nullability) are not listed. The “after” signatures are expressed in the specification notation.
A.1 Terminal Reader API (Java 2.1.0 → 3.0.0)
| Element in production | What it becomes |
|---|---|
ReaderApiFactory.createCardSelectionManager() | Removed → createSingleCardSelectionManager(), createSequentialCardSelectionManager(), createMultichannelCardSelectionManager() |
ReaderApiFactory.createBasicCardSelector(), createIsoCardSelector() | Removed (selectors = data classes) |
| — | Added: ReaderApiFactory.getCardReaderProvider() → CardReaderProvider; CardReaderProvider interface |
ConfigurableCardReader (activateProtocol, deactivateProtocol, getCurrentProtocol) | Removed |
ObservableCardReader.setReaderObservationExceptionHandler, addObserver, removeObserver, clearObservers, countObservers | Removed |
ObservableCardReader.startCardDetection(DetectionMode) | → startCardDetection(settings: CardDetectionSettings, eventHandler: CardReaderEventHandler) |
ObservableCardReader.finalizeCardProcessing() | → endCardProcessing() |
| — | Added: ObservableCardReader.clearScheduledCardSelectionScenario() |
ObservableCardReader.DetectionMode (REPEATING, SINGLESHOT) | → DetectionMode (REPEATING, SINGLE_SHOT) |
ObservableCardReader.NotificationMode | → CardPresenceNotificationPolicy (namespace reader.selection) |
CardReaderEvent (interface) | → CardReaderEvent data class (readerName, type, scheduledCardSelectionsResponse?) |
CardReaderEvent.Type (…, UNAVAILABLE) | → CardReaderEventType (…, READER_UNREGISTERED) |
ChannelControl | Removed |
CardCommunicationException, ReaderCommunicationException, InvalidCardResponseException | → CardCommunication, ReaderCommunication, InvalidCardResponse |
ReaderProtocolNotSupportedException | Removed |
reader.selection.InvalidCardResponseException | Removed (duplicate) |
CardSelectionManager.setMultipleSelectionMode(), prepareReleaseChannel() | Removed |
CardSelectionManager.prepareSelection(CardSelector<?>, CardSelectionExtension) → int | → prepareSelectionCase(selectionCaseId: Int, cardSelector: CardSelector, cardSelectionExtension: CardSelectionExtension) → Self |
CardSelectionManager.importCardSelectionScenario(String) → int | → importCardSelectionScenario(cardSelectionScenario: String) → Self (replaces the scenario) |
CardSelectionManager.processCardSelectionScenario(CardReader) | → processCardSelectionScenario(reader) on SingleCardSelectionManager / SequentialCardSelectionManager; processCardSelectionScenario(reader, channelSelectionPolicy) on MultichannelCardSelectionManager |
CardSelectionManager.scheduleCardSelectionScenario(ObservableCardReader, NotificationMode) | → scheduleCardSelectionScenario(observableCardReader, cardPresenceNotificationPolicy) on the single-channel managers |
CardSelectionManager.parseScheduledCardSelectionsResponse(...) | → on the single-channel managers, returns the typed result |
CardSelectionManager.importProcessedCardSelectionScenario(String) | → on each manager, returns the typed result |
CardSelectionResult (getSmartCards, getActiveSmartCard, getActiveSelectionIndex) | Removed → SingleCardSelectionResult, SequentialCardSelectionResult, MultichannelCardSelectionResult |
| — | Added: ChannelSelectionPolicy |
CardSelector<T> (filterByCardProtocol, filterByPowerOnData) | → sealed interface CardSelector; filterByCardProtocol removed; filterByPowerOnData → powerOnDataRegex property; cardType property added |
BasicCardSelector (interface) | → data class (cardType?, powerOnDataRegex?) |
CommonIsoCardSelector<T> (filterByDfName(byte[]), filterByDfName(String), setFileOccurrence, setFileControlInformation) | Removed → dfName: ByteArray?, fileOccurrence, fileControlInformation properties of IsoCardSelector; filterByDfName(String) removed |
CommonIsoCardSelector.FileOccurrence, .FileControlInformation | → FileOccurrence, FileControlInformation |
IsoCardSelector (interface) | → data class |
SmartCard.getPowerOnData() | → getPowerOnData() → String?; isActive() → Boolean added |
IsoSmartCard.getSelectApplicationResponse() | → getSelectApplicationResponse() → ByteArray?; isBasicChannel() → Boolean added |
CardReaderObserverSpi.onReaderEvent(CardReaderEvent) | → CardReaderEventHandler.onReaderEvent(cardReaderEvent: CardReaderEvent) |
CardReaderObservationExceptionHandlerSpi.onReaderObservationError(String contextInfo, String readerName, Throwable e) | → CardReaderEventHandler.onReaderError(context: String, readerName: String, error: Any) |
CardTransactionManager<T>.processCommands(ChannelControl) → T | → CardTransactionManager.processCommands() → Unit (non-generic) |
| — | Added: IsoCardTransactionManager, MultichannelCardTransactionManager |
| — | Added: CardDetectionSettings data class |
A.2 Terminal Card API (Java 2.0.1 → 3.0.0)
| Element in production | What it becomes |
|---|---|
ApduRequestSpi (getApdu, getSuccessfulStatusWords, getInfo) | → ApduRequest data class (apdu, successfulStatusWords = setOf(0x9000), info: String? = null) + apduExchangeMaxDuration: Long? = null |
ApduResponseApi (getApdu, getDataOut, getStatusWord, Serializable) | → ApduResponse data class (apdu) + apduExchangeDuration: Long?; getDataOut, getStatusWord removed |
CardRequestSpi (getApduRequests, stopOnUnsuccessfulStatusWord) | → CardRequest data class |
CardResponseApi (getApduResponses, isLogicalChannelOpen) | → CardResponse data class (apduResponses); isLogicalChannelOpen removed |
CardSelectionRequestSpi (getSuccessfulSelectionStatusWords, getCardRequest) | → CardSelectionRequest data class (successfulSelectionStatusWords = setOf(0x9000), cardRequest: CardRequest? = null) |
CardSelectionResponseApi (getPowerOnData, getSelectApplicationResponse, hasMatched, getCardResponse) | → CardSelectionResponse data class (same information, explicit nullability) + channel: Int |
CardSelectionExtensionSpi.getCardSelectionRequest(), parse(CardSelectionResponseApi) | → CardSelectionRequest / CardSelectionResponse types |
SmartCardSpi | deactivate() → Unit added |
| — | Added: MultichannelSmartCardSpi (getChannel() → Int) |
ProxyReaderApi.transmitCardRequest(CardRequestSpi, ChannelControl), releaseChannel() | Removed → transmitCardRequest(cardRequest, smartCard), transmitCardRequestAndCloseChannel(cardRequest, multichannelSmartCard), closeChannel(multichannelSmartCard) |
ChannelControl | Removed |
AbstractApduException (getCardResponse, isCardResponseComplete) | Removed; cardResponse: CardResponse? and isCardResponseComplete: Boolean properties carried by the APDU errors |
CardBrokenCommunicationException, ReaderBrokenCommunicationException, UnexpectedStatusWordException, ParseException | → CardBrokenCommunication (also raised if the card is no longer active), ReaderBrokenCommunication, UnexpectedStatusWord, Parse |
| — | Added: ApduExchangeDurationExceeded error |
A.3 Terminal Calypso Card API (Java 2.2.0 → 3.0.0)
| Element in production | What it becomes |
|---|---|
CalypsoCardApiFactory.createSearchCommandData() | Removed |
CalypsoCard.getProductType() → CalypsoCard.ProductType | → getProductType() → CalypsoCardProductType |
CalypsoCard.getDirectoryHeader(), getFileBySfi, getFileByLid, getSvLoadLogRecord, getSvDebitLogLastRecord | → explicit nullable returns |
| — | Added: CalypsoCard.getCounterValuesBySfi, getCounterValuesByLid, getMatchingRecordNumbers(commandId), getSvLoadLogRecordRawData, getSvDebitLogLastRecordRawData, getSvDebitLogAllRecordsRawData |
CalypsoCard.ProductType | → CalypsoCardProductType |
TransactionManager.prepareDecreaseCounters(sfi, counterNumberToDecValueMap), prepareIncreaseCounters(sfi, counterNumberToIncValueMap) | → parameters renamed decrementValues, incrementValues |
SymmetricCryptoSecuritySetting, AsymmetricCryptoSecuritySetting | → SymmetricCryptoSecuritySettings, AsymmetricCryptoSecuritySettings |
CalypsoCardApiFactory.createSymmetricCryptoSecuritySetting(...), createAsymmetricCryptoSecuritySetting(...) | → createSymmetricCryptoSecuritySettings(...), createAsymmetricCryptoSecuritySettings(...); securitySetting parameter → securitySettings in createSecure…TransactionManager |
CalypsoCardSelectionExtension.prepareSelectFile(short) / prepareSelectFile(SelectFileControl selectControl) | → prepareSelectFileByLid(lid) / prepareSelectFileByControl(selectFileControl) |
DirectoryHeader (interface; getKif(level), getKvc(level)) | → data class; kifByAccessLevel, kvcByAccessLevel: Map<WriteAccessLevel, Byte> |
ElementaryFile (interface; getData()) | → data class (sfi, header?, records: SortedMap<Int, ByteArray>) |
ElementaryFile.Type | → ElementaryFileType |
FileData (all operations) | Removed (see §12.2) |
FileHeader (interface) | → data class (efType: ElementaryFileType) |
SvDebitLogRecord, SvLoadLogRecord (interfaces, getRawData) | → data classes without rawData |
SearchCommandData.setSfi, startAtRecord, setOffset, enableRepeatedOffset, setSearchData, setMask, fetchFirstMatchingResult | → properties of the SearchCommandData data class: sfi, startAtRecord = 1, offset = 0, repeatedOffset = false, searchData, mask: ByteArray? = null, fetchFirstMatchingResult = false |
SearchCommandData.getMatchingRecordNumbers() | → CalypsoCard.getMatchingRecordNumbers(commandId: Int) → List<Int>? |
TransactionManager<T> and generic sub-interfaces | → non-generic, Self returns; TransactionManager extends IsoCardTransactionManager |
TransactionManager.prepareSelectFile(short) / (SelectFileControl) | → prepareSelectFileByLid / prepareSelectFileByControl |
TransactionManager.prepareSearchRecords(SearchCommandData) | → prepareSearchRecords(commandId: Int, data: SearchCommandData) |
TransactionManager.processCommands(ChannelControl) | Removed (inherited CardTransactionManager.processCommands()) |
| — | Added: TransactionManager.getSecureSessionState() → SecureSessionState; SecureSessionState enumeration |
SecureTransactionManager.getCryptoExtension(Class<E>) → E | → getCryptoExtension() → CardTransactionCryptoExtension |
SecureSymmetricCryptoTransactionManager.prepareSvGet(SvOperation, SvAction) | → prepareSvGet(svOperation: SvOperation) |
prepareSvReload(int), prepareSvDebit(int) | Removed |
| — | Added: prepareSvUndebit(amount, date, time) |
SvAction | Removed |
SvOperation.DEBIT | → SvOperation.DEBIT_UNDEBIT |
| — | Added: SecuritySettings interface, parent of SymmetricCryptoSecuritySettings and AsymmetricCryptoSecuritySettings, carrying assignOpenSecureSessionMaxDurationByCsn/ByFci(...), assignCloseSecureSessionMaxDurationByCsn/ByFci(...), authorizeFileNotFoundError() and authorizeRecordNotFoundError() |
| — | Added: SymmetricCryptoSecuritySettings.assignSvCommandMaxDurationByCsn/ByFci(...); the session bounds are inherited from SecuritySettings |
ChannelControl | Removed |
CardIOException, ReaderIOException, UnexpectedCommandStatusException, SelectFileException | Removed |
CardSignatureNotVerifiableException, CryptoException, CryptoIOException, InconsistentDataException, InvalidCardSignatureException, InvalidCertificateException, InvalidPinException, SessionBufferOverflowException, UnauthorizedKeyException | → same names without the Exception suffix |
A.4 Terminal Calypso Crypto Legacy SAM API (Java 1.0.0 → 2.0.0)
| Element in production | What it becomes |
|---|---|
LegacySamApiFactory.createSecuritySetting(), createKeyPairContainer(), createLegacyCardCertificateComputationData(), createBasicSignatureComputationData(), createTraceableSignatureComputationData(), createBasicSignatureVerificationData(), createTraceableSignatureVerificationData() | Removed (data classes) |
LegacySam.getProductType() → LegacySam.ProductType | → getProductType() → LegacySamProductType |
LegacySam.getCounter(int), getCounterCeiling(int) | Removed (use getCounters(), getCounterCeilings()) |
LegacySam.getCounterIncrementAccess(int) | → getCounterIncrementAccesses() → SortedMap<Int, CounterIncrementAccess> |
LegacySam.getSamParameters() → SamParameters | → getSamParameters() → ByteArray?; SamParameters removed |
LegacySam.getSystemKeyParameter(SystemKeyType) | → getSystemKeyParameters(systemKeyType) → KeyParameters? |
LegacySam.getWorkKeyParameter(int) / (byte, byte) | → getWorkKeyParametersByRecordNumber / getWorkKeyParametersByKifKvc |
| — | Added: LegacySam.getSystemKeyParametersRawData, getWorkKeyParametersRawDataByRecordNumber, getWorkKeyParametersRawDataByKifKvc, getKeyPair(commandId), getComputedCardCertificate(commandId), getSignature(commandId), getSignedData(commandId), isSignatureValid(commandId) |
LegacySam.ProductType | → LegacySamProductType |
KeyParameter (interface; getRawData, getParameterValue(int)) | → KeyParameters data class (kif, kvc, algorithm, parameterValues) |
LegacySamSelectionExtension.setUnlockData(String, ProductType) | → setUnlockDataForProductType(unlockData, productType) |
LegacySamSelectionExtension.setStaticUnlockDataProvider(provider) / setDynamicUnlockDataProvider(provider) | → setStaticUnlockDataProviderWithDeferredReader(provider) / setDynamicUnlockDataProviderWithDeferredReader(provider) |
LegacySamSelectionExtension.prepareReadWorkKeyParameters(int) / (byte, byte) | → prepareReadWorkKeyParametersByRecordNumber / prepareReadWorkKeyParametersByKifKvc |
LegacySamRevocationServiceSpi.isSamRevoked(byte[]) | Removed (isSamRevoked(serialNumber, counterValue: Int) remains) |
TransactionManager<T> and generic sub-interfaces | → non-generic, Self returns |
TransactionManager.processCommands(), processCommands(ChannelControl) | Removed (inherited CardTransactionManager.processCommands()) |
ReadTransactionManager.prepareReadWorkKeyParameters(int) / (byte, byte) | → …ByRecordNumber / …ByKifKvc |
FreeTransactionManager.prepareGenerateCardAsymmetricKeyPair(KeyPairContainer) | → prepareGenerateCardAsymmetricKeyPair(commandId: Int) |
FreeTransactionManager.prepareComputeCardCertificate(data) | → prepareComputeCardCertificate(commandId: Int, data) |
FreeTransactionManager / CardTransactionLegacySamExtension .prepareComputeSignature(data), .prepareVerifySignature(data) | → prepareComputeSignature(commandId: Int, data), prepareVerifySignature(commandId: Int, data) |
SecureWriteTransactionManager.prepareTransferWorkKeyDiversified(…, diversifier) | → prepareTransferWorkKeyDiversifiedWithSpecificDiversifier(…, diversifier) |
KeyPairContainer | Removed |
LegacyCardCertificateComputationData.setCardPublicKey, setStartDate, setEndDate, setCardAid, setCardSerialNumber, setCardStartupInfo | → properties of the data class: cardPublicKey, startDate, endDate, cardAid, cardSerialNumber, cardStartupInfo |
LegacyCardCertificateComputationData.getCertificate() | → LegacySam.getComputedCardCertificate(commandId: Int) → ByteArray? |
SignatureComputationData<T> | → sealed interface SignatureComputationData |
SignatureComputationData.setData(byte[] data, byte kif, byte kvc), setSignatureSize(int), setKeyDiversifier(byte[]) | → properties data, kif, kvc, signatureSize = 8, keyDiversifier: ByteArray? = null of the data classes |
SignatureComputationData.getSignature() | → LegacySam.getSignature(commandId: Int) → ByteArray? |
BasicSignatureComputationData, TraceableSignatureComputationData | → data classes implementing SignatureComputationData |
TraceableSignatureComputationData.withSamTraceabilityMode(int offset, SamTraceabilityMode mode), withoutBusyMode() | → properties traceabilityOffset = 0, samTraceabilityMode: SamTraceabilityMode? = null, busyMode = true |
TraceableSignatureComputationData.getSignedData() | → LegacySam.getSignedData(commandId: Int) → ByteArray? |
SignatureVerificationData<T> | → sealed interface SignatureVerificationData |
SignatureVerificationData.setData(byte[] data, byte[] signature, byte kif, byte kvc), setKeyDiversifier(byte[]) | → properties data, signature, kif, kvc, keyDiversifier: ByteArray? = null of the data classes |
SignatureVerificationData.isSignatureValid() | → LegacySam.isSignatureValid(commandId: Int) → Boolean? |
BasicSignatureVerificationData, TraceableSignatureVerificationData | → data classes implementing SignatureVerificationData |
TraceableSignatureVerificationData.withSamTraceabilityMode(int offset, SamTraceabilityMode mode, LegacySamRevocationServiceSpi service), withoutBusyMode() | → properties traceabilityOffset = 0, samTraceabilityMode: SamTraceabilityMode? = null, samRevocationService: LegacySamRevocationServiceSpi? = null, busyMode = true |
SecuritySetting.setControlSamResource(samReader, controlSam) | → LegacySamSecuritySettings data class (samReader, controlSam); securitySetting parameter → securitySettings in createSecureWriteTransactionManager and createAsyncTransactionCreatorManager |
ReaderIOException, SamIOException, UnexpectedCommandStatusException | Removed |
InconsistentDataException, InvalidSignatureException, SamRevokedException | → InconsistentData, InvalidSignature, SamRevoked |
A.5 Terminal Calypso Crypto Symmetric API (Java 0.1.1 → 0.2.0)
| Element in production | What it becomes |
|---|---|
SvCommandSecurityDataApi.getSvGetRequest(), getSvGetResponse(), getSvCommandPartialRequest() (inputs) | → svGetRequest, svGetResponse, svCommandPartialRequest parameters of computeSvCommandSecurityData |
SvCommandSecurityDataApi.setSerialNumber, setTransactionNumber, setTerminalChallenge, setTerminalSvMac (outputs) | → serialNumber, transactionNumber, terminalChallenge, terminalSvMac properties of the SvCommandSecurityData data class (namespace calypso.crypto.symmetric.spi) |
SymmetricCryptoCardTransactionManagerSpi.computeSvCommandSecurityData(SvCommandSecurityDataApi) → void | → computeSvCommandSecurityData(svGetRequest, svGetResponse, svCommandPartialRequest) → SvCommandSecurityData |
SymmetricCryptoCardTransactionManagerSpi.cipherPinForPresentation(…, Byte kif, Byte kvc), cipherPinForModification(…, Byte kif, Byte kvc) | → non-nullable kif: Byte, kvc: Byte |
SymmetricCryptoCardTransactionManagerFactorySpi.createCardTransactionManager(…, List<byte[]> transactionAuditData) | → transactionAuditData: MutableList<ByteArray> |
SymmetricCryptoException, SymmetricCryptoIOException | → SymmetricCrypto, SymmetricCryptoIO |
A.6 Terminal Calypso Crypto Asymmetric API (Java 0.2.0 → 0.3.0)
| Element in production | What it becomes |
|---|---|
CaCertificateContentSpi.getPublicKey, getPublicKeyReference, getStartDate, getEndDate, isAidTruncated, getAid, isCaCertificatesAuthenticationAllowed, isCardCertificatesAuthenticationAllowed | → properties of the CaCertificateContent data class: publicKey, publicKeyReference, startDate, endDate, isAidTruncated, aid: ByteArray?, isCaCertificatesAuthenticationAllowed, isCardCertificatesAuthenticationAllowed |
CaCertificateContentSpi.isAidCheckRequested() | Removed (deducible from aid, null if the check is not requested) |
CaCertificateSpi.checkCertificateAndGetContent(CaCertificateContentSpi) → CaCertificateContentSpi | → checkCertificateAndGetContent(issuerCertificateContent: CaCertificateContent) → CaCertificateContent |
PcaCertificateSpi.checkCertificateAndGetContent() → CaCertificateContentSpi | → … → CaCertificateContent |
CardCertificateSpi.checkCertificateAndGetPublicKey(CaCertificateContentSpi) → CardPublicKeySpi | → checkCertificateAndGetPublicKey(issuerCertificateContent: CaCertificateContent) → ByteArray |
CardPublicKeySpi (getRawValue) | Removed |
AsymmetricCryptoCardTransactionManagerSpi.initTerminalPkiSession(CardPublicKeySpi) | → initTerminalPkiSession(cardPublicKey: ByteArray) |
AsymmetricCryptoException, CertificateValidationException | → AsymmetricCrypto, CertificateValidation |
A.7 Terminal Generic Card API (Java 1.0.0 → 2.0.0)
| Element in production | What it becomes |
|---|---|
CardTransactionManager (extends the Reader API CardTransactionManager<…>) | → GenericCardTransactionManager (extends IsoCardTransactionManager) |
prepareApdu(String) | Removed |
prepareApdu(byte[]) | → prepareCommand(apdu: ByteArray) |
prepareApdu(byte cla, byte ins, byte p1, byte p2, byte[] dataIn, Byte le) | Removed |
| — | Added: prepareCommandWithId(commandId, apdu), prepareCommandWithMaxDuration(commandId, apdu, maxDuration), getLastExecutionResponse(commandId) → ByteArray? |
getResponsesAsByteArrays() | → getLastExecutionResponses() → List<ByteArray> |
getResponsesAsHexStrings() | Removed |
GenericCardApiFactory.createCardTransaction(reader, card) | → createGenericCardTransactionManager(reader, card) |
A.8 Terminal Storage Card API (Java 1.2.0 → 2.0.0)
| Element in production | What it becomes |
|---|---|
ProductType (methods getBlockCount, getBlockSize, hasSystemBlock, hasWriteAcknowledgment, hasAuthentication) | → StorageCardProductType with properties blockCount, blockSize, hasSystemBlock, hasWriteAcknowledgment, hasAuthentication |
StorageCardApiFactory.createStorageCardSelectionExtension(ProductType) | → productType: StorageCardProductType parameter |
StorageCard.getUID() | → getUid() |
StorageCard.getSystemBlock(), getBlock(int), getBlocks(int, int) | → ByteArray? returns |
StorageCardSelectionExtension / StorageCardTransactionManager .prepareMifareClassicAuthenticate(…, byte[] key) / (…, int keyNumber) | → prepareMifareClassicAuthenticateWithKey / prepareMifareClassicAuthenticateWithKeyNumber |
StorageCardTransactionManager (extends CardTransactionManager<…>) | → non-generic, Self returns |
StorageCardTransactionManager.prepareReadSystemBlock(), prepareWriteSystemBlock(byte[]) (deprecated) | Removed |
StorageCardTransactionManager.prepareSt25WriteSystemBlock(byte[]) | → prepareSt25WriteSystemBlock(commandId: Int, data: ByteArray) |
| — | Added: StorageCardSecuritySettings data class (readCommandMaxDurations); securitySettings parameter added to StorageCardApiFactory.createStorageCardTransactionManager(...) |
StorageCardTransactionManager.prepareWriteBlocks(int, byte[]) | → prepareWriteBlocks(commandId: Int, fromBlockAddress: Int, data: ByteArray) |
StorageCardException (getBlockAddress) | Removed; the errors carry blockAddress: Int? and commandId: Int? |
SCAuthenticationFailedException (extends CardCommunicationException) | → StorageCardAuthenticationFailed (no parent error) |
SCCardCommunicationException, SCInvalidCardResponseException, SCReaderCommunicationException | → StorageCardCardCommunication, StorageCardInvalidCardResponse, StorageCardReaderCommunication (parents unchanged) |
A.9 Terminal Reader Definitions API (new, 1.0.0)
| Element | Content |
|---|---|
ReaderDefinitionsApiProperties | VERSION constant |
RfTechnology | ISO_14443_AB, INNOVATRON_B_PRIME, FELICA, ISO_15693 |
CardType | ISO_7816_3, ISO_14443_4, ISO_14443_3A_MIFARE_CLASSIC_1K, ISO_14443_3A_MIFARE_CLASSIC_4K, ISO_14443_3A_MIFARE_ULTRALIGHT, ISO_14443_3B_ST25_SRT512, INNOVATRON_B_PRIME, FELICA, ISO_15693, UNKNOWN |
End of document.