From 823b20f53e1b3f542dc56b4ea919376d2ea4a8ce Mon Sep 17 00:00:00 2001 From: andrei-cristea Date: Thu, 16 Feb 2023 10:44:19 +0100 Subject: [PATCH] feat: simplify the transaction API by using a unique "process" method (#52) Co-authored-by: Jean-Pierre Fortune --- CHANGELOG.md | 18 ++ gradle.properties | 2 +- .../calypso/CalypsoApiProperties.java | 2 +- .../calypso/card/CalypsoCardSelection.java | 65 ++++++-- .../transaction/CardSecuritySetting.java | 14 ++ .../transaction/CardTransactionManager.java | 155 ++++++++++++++++-- .../transaction/CommonTransactionManager.java | 39 +++++ 7 files changed, 268 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eca3c5..5067b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `CommonTransactionManager.processCommands(boolean)` method. +- `CardTransactionManager.prepareVerifyPin` method. +- `CardTransactionManager.prepareChangePin` method. +- `CardTransactionManager.prepareChangeKey` method. +- `CardTransactionManager.prepareOpenSecureSession` method. +- `CardTransactionManager.prepareCloseSecureSession` method. +- `CardTransactionManager.prepareCancelSecureSession` method. +- `CardSecuritySetting.disableReadOnSessionOpening` method. +### Deprecated +- `CommonTransactionManager.processCommands()` method. +- `CardTransactionManager.prepareReleaseCardChannel` method. +- `CardTransactionManager.processVerifyPin` method. +- `CardTransactionManager.processChangePin` method. +- `CardTransactionManager.processChangeKey` method. +- `CardTransactionManager.processOpening` method. +- `CardTransactionManager.processClosing` method. +- `CardTransactionManager.processCancel` method. ## [1.5.0] - 2022-12-22 ### Added diff --git a/gradle.properties b/gradle.properties index 77133d6..a908ef8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group = org.calypsonet.terminal title = Calypsonet Terminal Calypso API description = API defining the needed interfaces to manage Calypso cards -version = 1.5.1 +version = 1.6.0 javaSourceLevel = 1.6 javaTargetLevel = 1.6 diff --git a/src/main/java/org/calypsonet/terminal/calypso/CalypsoApiProperties.java b/src/main/java/org/calypsonet/terminal/calypso/CalypsoApiProperties.java index 244cc0e..c4e0b58 100644 --- a/src/main/java/org/calypsonet/terminal/calypso/CalypsoApiProperties.java +++ b/src/main/java/org/calypsonet/terminal/calypso/CalypsoApiProperties.java @@ -23,7 +23,7 @@ public class CalypsoApiProperties { * * @since 1.0.0 */ - public static final String VERSION = "1.5"; + public static final String VERSION = "1.6"; /** Private constructor */ private CalypsoApiProperties() {} diff --git a/src/main/java/org/calypsonet/terminal/calypso/card/CalypsoCardSelection.java b/src/main/java/org/calypsonet/terminal/calypso/card/CalypsoCardSelection.java index 4fc70e6..677bb9a 100644 --- a/src/main/java/org/calypsonet/terminal/calypso/card/CalypsoCardSelection.java +++ b/src/main/java/org/calypsonet/terminal/calypso/card/CalypsoCardSelection.java @@ -190,6 +190,22 @@ public interface CalypsoCardSelection extends CardSelection { */ CalypsoCardSelection prepareSelectFile(SelectFileControl selectControl); + /** + * Adds a command APDU to retrieve the data indicated by the provided tag. + * + *

This method can be used to obtain FCI information when it is not provided directly by the + * select application (e.g. OMAPI case). + * + *

Caution: the resulting APDU command must be compliant with PRIME revision 3 cards. + * Therefore, the command may be rejected by some earlier revision cards. + * + * @param tag The tag to use. + * @return The object instance. + * @throws IllegalArgumentException If tag is null. + * @since 1.0.0 + */ + CalypsoCardSelection prepareGetData(GetDataTag tag); + /** * Adds a command APDU to read a single record from the indicated EF. * @@ -226,21 +242,40 @@ public interface CalypsoCardSelection extends CardSelection { */ CalypsoCardSelection prepareReadRecord(byte sfi, int recordNumber); - /** - * Adds a command APDU to retrieve the data indicated by the provided tag. - * - *

This method can be used to obtain FCI information when it is not provided directly by the - * select application (e.g. OMAPI case). - * - *

Caution: the resulting APDU command must be compliant with PRIME revision 3 cards. - * Therefore, the command may be rejected by some earlier revision cards. - * - * @param tag The tag to use. - * @return The object instance. - * @throws IllegalArgumentException If tag is null. - * @since 1.0.0 - */ - CalypsoCardSelection prepareGetData(GetDataTag tag); + // /** + // * Adds an APDU command to attempt a secure session pre-opening. For cards that support this + // * feature, this optimizes exchanges with the card in the case of deterministic secure + // sessions + // * that can be executed in a single step. + // * + // *

The use of this method or one of the following methods is a prerequisite for the use of + // the + // * {@link CardTransactionManager#processPreOpenedSecureSession()} method: + // * + // *

+ // * + // * It is not advised to use it in other cases. + // * + // *

The secure session opening which will be done by {@link + // * CardTransactionManager#processPreOpenedSecureSession()} will use the same parameters (same + // * {@link WriteAccessLevel}, no record reading). + // * + // * @param writeAccessLevel The write access level. + // * @return The object instance. + // * @throws IllegalArgumentException If writeAccessLevel is null. + // * @throws IllegalStateException If "Pre-Open" command is already prepared. + // * @see #preparePreOpenSecureSession(WriteAccessLevel, byte, int) + // * @see CardTransactionManager#preparePreOpenSecureSession(WriteAccessLevel) + // * @see CardTransactionManager#preparePreOpenSecureSession(WriteAccessLevel, byte, int) + // * @see CardTransactionManager#processPreOpenedSecureSession() + // * @since 1.6.0 + // */ + // CalypsoCardSelection preparePreOpenSecureSession(WriteAccessLevel writeAccessLevel); /** * Navigation options through the different applications contained in the card according to the diff --git a/src/main/java/org/calypsonet/terminal/calypso/transaction/CardSecuritySetting.java b/src/main/java/org/calypsonet/terminal/calypso/transaction/CardSecuritySetting.java index 5dc8e82..8703afe 100644 --- a/src/main/java/org/calypsonet/terminal/calypso/transaction/CardSecuritySetting.java +++ b/src/main/java/org/calypsonet/terminal/calypso/transaction/CardSecuritySetting.java @@ -84,6 +84,20 @@ public interface CardSecuritySetting extends CommonSecuritySettingBy default, this optimization is performed when the command that follows the session opening + * is a "Read Record" command. + * + *

This mechanism may in some cases be incompatible with the security requirements. + * + * @return The current instance. + * @since 1.6.0 + */ + CardSecuritySetting disableReadOnSessionOpening(); + /** * Defines for a given write access level the KIF value to use for cards that only provide KVC. * diff --git a/src/main/java/org/calypsonet/terminal/calypso/transaction/CardTransactionManager.java b/src/main/java/org/calypsonet/terminal/calypso/transaction/CardTransactionManager.java index f50b8ba..e40b929 100644 --- a/src/main/java/org/calypsonet/terminal/calypso/transaction/CardTransactionManager.java +++ b/src/main/java/org/calypsonet/terminal/calypso/transaction/CardTransactionManager.java @@ -890,7 +890,9 @@ CardTransactionManager prepareDecreaseCounters( * * @return The current instance. * @since 1.0.0 + * @deprecated Use {@link #processCommands(boolean)} method instead. */ + @Deprecated CardTransactionManager prepareReleaseCardChannel(); /** @@ -1030,9 +1032,33 @@ CardTransactionManager prepareDecreaseCounters( * @throws UnexpectedCommandStatusException If a command returns an unexpected status. * @throws InconsistentDataException If inconsistent data have been detected. * @since 1.0.0 + * @deprecated Use {@link #prepareVerifyPin(byte[])} method instead. */ + @Deprecated CardTransactionManager processVerifyPin(byte[] pin); + /** + * Schedules the execution of a PIN verification command in order to authenticate the cardholder + * and/or unlock access to certain card files. + * + *

This command can be performed both in and out of a secure session. The PIN code can be + * transmitted in plain text or encrypted according to the parameter set in {@link + * CardSecuritySetting}. By default, the transmission is encrypted. + * + *

If the execution is done out of session but an encrypted transmission is requested, then + * CardTransactionManager must be constructed with {@link CardSecuritySetting}. + * + *

If CardTransactionManager is constructed without {@link CardSecuritySetting} the + * transmission in done in plain. + * + * @param pin The PIN code value (4-byte long byte array). + * @return The current instance. + * @throws UnsupportedOperationException If the PIN feature is not available for this card. + * @throws IllegalArgumentException If the provided argument is out of range. + * @since 1.6.0 + */ + CardTransactionManager prepareVerifyPin(byte[] pin); + /** * Replaces the current PIN with the new value provided. * @@ -1054,9 +1080,31 @@ CardTransactionManager prepareDecreaseCounters( * @throws UnexpectedCommandStatusException If a command returns an unexpected status. * @throws InconsistentDataException If inconsistent data have been detected. * @since 1.0.0 + * @deprecated Use {@link #prepareChangePin(byte[])} method instead. */ + @Deprecated CardTransactionManager processChangePin(byte[] newPin); + /** + * Schedules the execution of a PIN modification command to replace the current PIN with the new + * value provided. + * + *

This command can be performed only out of a secure session. The new PIN code can be + * transmitted in plain text or encrypted according to the parameter set in {@link + * CardSecuritySetting}. By default, the transmission is encrypted. + * + *

When the PIN is transmitted plain, this command must be preceded by a successful Verify PIN + * command (see {@link #prepareVerifyPin(byte[])}). + * + * @param newPin The new PIN code value (4-byte long byte array). + * @return The current instance. + * @throws UnsupportedOperationException If the PIN feature is not available for this card. + * @throws IllegalArgumentException If the provided argument is out of range. + * @throws IllegalStateException If the command is executed while a secure session is open. + * @since 1.6.0 + */ + CardTransactionManager prepareChangePin(byte[] newPin); + /** * Replaces one of the current card keys with another key present in the SAM. * @@ -1082,10 +1130,37 @@ CardTransactionManager prepareDecreaseCounters( * @throws UnexpectedCommandStatusException If a command returns an unexpected status. * @throws InconsistentDataException If inconsistent data have been detected. * @since 1.1.0 + * @deprecated Use {@link #prepareChangeKey(int, byte, byte, byte, byte)} method instead. */ + @Deprecated CardTransactionManager processChangeKey( int keyIndex, byte newKif, byte newKvc, byte issuerKif, byte issuerKvc); + /** + * Schedules the execution of a key loading command to replace one of the current card keys with + * another key present in the SAM. + * + *

This command can be performed only out of a secure session. + * + *

The change key process transfers the key from the SAM to the card. The new key is + * diversified by the SAM from a primary key and encrypted using the indicated issuer key to + * secure the transfer to the card. All provided KIFs and KVCs must be present in the SAM. + * + * @param keyIndex The index of the key to be replaced (1 for the issuer key, 2 for the load key, + * 3 for the debit key). + * @param newKif The KIF of the new key. + * @param newKvc The KVC of the new key. + * @param issuerKif The KIF of the current card's issuer key. + * @param issuerKvc The KVC of the current card's issuer key. + * @return The current instance. + * @throws UnsupportedOperationException If the Change Key command is not available for this card. + * @throws IllegalArgumentException If the provided key index is out of range. + * @throws IllegalStateException If the command is executed while a secure session is open. + * @since 1.6.0 + */ + CardTransactionManager prepareChangeKey( + int keyIndex, byte newKif, byte newKvc, byte issuerKif, byte issuerKvc); + /** * Opens a Calypso Secure Session and then executes all previously prepared commands. * @@ -1093,9 +1168,11 @@ CardTransactionManager processChangeKey( * *

* @@ -1142,14 +1219,6 @@ CardTransactionManager processChangeKey( *
  • Receiving grouped responses and updating {@link CalypsoCard} with the collected data. * * - * For optimization purposes, if the first command prepared is the reading of a single record of a - * card file then this one is replaced by a setting of the session opening command allowing the - * retrieval of this data in response to this command. - * - *

    Please note that the CAAD mechanism may require a file to be read before being modified. For - * this mechanism to work properly, this reading must not be placed in the first position of the - * prepared commands in order to be correctly taken into account by the SAM. - * *

    Other operations carried out * *

      @@ -1191,9 +1260,40 @@ CardTransactionManager processChangeKey( * @throws SelectFileException If a "Select File" prepared card command indicated that the file * was not found. * @since 1.0.0 + * @deprecated Use {@link #prepareOpenSecureSession(WriteAccessLevel)} method instead. */ + @Deprecated CardTransactionManager processOpening(WriteAccessLevel writeAccessLevel); + /** + * Schedules the execution of a secure session opening command. + * + *

      This feature is only available for a transaction initialized in secure mode. + * + *

      The secure session will be opened with the provided {@link WriteAccessLevel} depending on + * whether it is a personalization, reload or debit transaction profile. + * + *

      Note that if the next prepared command is a "Read One Record" or "Read One Or More + * Counters", then it will by default be merged with the "Open Secure Session" command for + * optimization purposes. + * + *

      This mechanism may in some cases be incompatible with the security constraints and can be + * disabled via the {@link CardSecuritySetting#disableReadOnSessionOpening()} method. + * + * @param writeAccessLevel The write access level to be used. + * @return The current instance. + * @throws IllegalArgumentException If the provided argument is null. + * @throws IllegalStateException In the following cases: + *

        + *
      • No {@link CardSecuritySetting} is available + *
      • A secure session opening is already prepared + *
      • A secure session is already opened + *
      + * + * @since 1.6.0 + */ + CardTransactionManager prepareOpenSecureSession(WriteAccessLevel writeAccessLevel); + /** * Terminates the Secure Session sequence started with {@link #processOpening(WriteAccessLevel)}. * @@ -1257,9 +1357,32 @@ CardTransactionManager processChangeKey( * @throws InvalidCardSignatureException If session is correctly closed but the card signature is * incorrect. * @since 1.0.0 + * @deprecated Use {@link #prepareCloseSecureSession()} method instead. */ + @Deprecated CardTransactionManager processClosing(); + /** + * Schedules the execution of a secure session closing command. + * + *

      The ratification mechanism is disabled by default but can be enabled via the {@link + * CardSecuritySetting#enableRatificationMechanism()} method. + * + *

      In this case, a ratification command is added after the "Close Secure Session" command when + * the communication is done in contactless mode. + * + * @return The current instance. + * @throws IllegalStateException In the following cases: + *

        + *
      • No secure session is opened and no secure session opening is prepared + *
      • A secure session closing is already prepared + *
      • A secure session canceling is prepared + *
      + * + * @since 1.6.0 + */ + CardTransactionManager prepareCloseSecureSession(); + /** * Aborts a Secure Session. * @@ -1273,6 +1396,18 @@ CardTransactionManager processChangeKey( * @throws CardIOException If a communication error with the card occurs. * @throws UnexpectedCommandStatusException If the command returns an unexpected status. * @since 1.0.0 + * @deprecated Use {@link #prepareCancelSecureSession()} method instead. */ + @Deprecated CardTransactionManager processCancel(); + + /** + * Schedules the execution of a secure session canceling command. + * + *

      This command will be executed in safe mode and will not raise any exceptions. + * + * @return The current instance. + * @since 1.6.0 + */ + CardTransactionManager prepareCancelSecureSession(); } diff --git a/src/main/java/org/calypsonet/terminal/calypso/transaction/CommonTransactionManager.java b/src/main/java/org/calypsonet/terminal/calypso/transaction/CommonTransactionManager.java index 1f83eec..353f153 100644 --- a/src/main/java/org/calypsonet/terminal/calypso/transaction/CommonTransactionManager.java +++ b/src/main/java/org/calypsonet/terminal/calypso/transaction/CommonTransactionManager.java @@ -114,6 +114,45 @@ public interface CommonTransactionManager< * @throws SelectFileException If a "Select File" prepared card command indicated that the file * was not found. * @since 1.2.0 + * @deprecated Use {@link #processCommands(boolean)} method instead. */ + @Deprecated T processCommands(); + + /** + * Processes all previously prepared commands and closes the physical channel if requested. + * + *

      All APDUs corresponding to the prepared commands are sent to the card, their responses are + * retrieved and used to update the card image associated with the transaction. + * + *

      For read commands, the card image is updated with the APDU output data. + * + *

      For write commands, the card image is updated with the APDU input data (provided the command + * is successful). + * + *

      The process is interrupted at the first failed command. + * + * @param closePhysicalChannel True if the physical channel must be closed after the operation. + * @return The current instance. + * @throws ReaderIOException If a communication error with the card reader or SAM reader occurs. + * @throws CardIOException If a communication error with the card occurs. + * @throws SamIOException If a communication error with the SAM occurs. + * @throws InvalidSignatureException If a signature associated to a prepared signature + * verification SAM command is invalid. + * @throws UnexpectedCommandStatusException If a command returns an unexpected status. + * @throws InconsistentDataException If inconsistent data have been detected. + * @throws UnauthorizedKeyException If the card requires an unauthorized session key. + * @throws SessionBufferOverflowException If a secure session is open and multiple session mode is + * disabled and the session buffer capacity is not sufficient. + * @throws CardSignatureNotVerifiableException If a secure session is open and multiple session + * mode is enabled and an intermediate session is correctly closed but the SAM is no longer + * available to verify the card signature. + * @throws InvalidCardSignatureException If a secure session is open and multiple session mode is + * enabled and an intermediate session is correctly closed but the card signature is + * incorrect. + * @throws SelectFileException If a "Select File" prepared card command indicated that the file + * was not found. + * @since 1.6.0 + */ + T processCommands(boolean closePhysicalChannel); }