Skip to content

Commit

Permalink
feat: switch to Keypop API (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanpierrefortune authored Dec 4, 2023
1 parent 55cd1f3 commit 70a82e4
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 73 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]
### Upgraded
- Calypsonet Terminal Reader API `1.3.0` -> Keypop Reader API `2.0.0`
- Calypsonet Terminal Calypso API `1.8.0` -> Keypop Calypso Card API `2.0.0`
- Keyple Service Library `2.3.1` -> `3.0.0`
- Keyple Calypso Card Library `2.3.5` -> `3.0.0`
- Keyple Util Library `2.3.0` -> `2.3.1`

### Added
New dependencies
- Keypop Crypto Legacy SAM API `0.3.0`
- Keyple Calypso Crypto LegacySAM Library `0.4.0`

## [2023.06.01]
### Upgraded
Expand Down
12 changes: 7 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,14 @@ dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))

// Keyple core
implementation("org.calypsonet.terminal:calypsonet-terminal-reader-java-api:1.3.0")
implementation("org.calypsonet.terminal:calypsonet-terminal-calypso-java-api:1.8.0")
implementation("org.eclipse.keypop:keypop-reader-java-api:2.0.0")
implementation("org.eclipse.keypop:keypop-calypso-card-java-api:2.0.0")
implementation("org.eclipse.keypop:keypop-calypso-crypto-legacysam-java-api:0.3.0")
implementation("org.eclipse.keyple:keyple-common-java-api:2.0.0")
implementation("org.eclipse.keyple:keyple-util-java-lib:2.3.0")
implementation("org.eclipse.keyple:keyple-service-java-lib:2.3.1")
implementation("org.eclipse.keyple:keyple-card-calypso-java-lib:2.3.5")
implementation("org.eclipse.keyple:keyple-util-java-lib:2.3.1")
implementation("org.eclipse.keyple:keyple-service-java-lib:3.0.0")
implementation("org.eclipse.keyple:keyple-card-calypso-java-lib:3.0.0")
implementation("org.eclipse.keyple:keyple-card-calypso-crypto-legacysam-java-lib:0.4.0")

// Keyple reader plugins
implementation("org.eclipse.keyple:keyple-plugin-android-nfc-java-lib:2.0.1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ import org.calypsonet.keyple.demo.control.data.model.Status
import org.calypsonet.keyple.demo.control.data.model.Validation
import org.calypsonet.keyple.demo.control.data.model.mapper.ContractMapper
import org.calypsonet.keyple.demo.control.data.model.mapper.ValidationMapper
import org.calypsonet.terminal.calypso.WriteAccessLevel
import org.calypsonet.terminal.calypso.card.CalypsoCard
import org.calypsonet.terminal.calypso.transaction.CardSecuritySetting
import org.calypsonet.terminal.reader.CardReader
import org.eclipse.keyple.card.calypso.CalypsoExtensionService
import org.eclipse.keypop.calypso.card.WriteAccessLevel
import org.eclipse.keypop.calypso.card.card.CalypsoCard
import org.eclipse.keypop.calypso.card.transaction.ChannelControl
import org.eclipse.keypop.calypso.card.transaction.SecureRegularModeTransactionManager
import org.eclipse.keypop.calypso.card.transaction.SymmetricCryptoSecuritySetting
import org.eclipse.keypop.calypso.card.transaction.TransactionManager
import org.eclipse.keypop.reader.CardReader
import timber.log.Timber

class CardRepository {
Expand All @@ -39,7 +42,7 @@ class CardRepository {
controlDateTime: LocalDateTime,
cardReader: CardReader,
calypsoCard: CalypsoCard,
cardSecuritySettings: CardSecuritySetting?,
cardSecuritySettings: SymmetricCryptoSecuritySetting?,
locations: List<Location>
): CardReaderResponse {

Expand All @@ -48,28 +51,28 @@ class CardRepository {
var validation: Validation? = null
var status: Status = Status.ERROR
val isSecureSessionMode = cardSecuritySettings != null
val calypsoExtensionService = CalypsoExtensionService.getInstance()
val calypsoCardApiFactory = CalypsoExtensionService.getInstance().calypsoCardApiFactory

try {
val cardTransaction =
val cardTransaction: TransactionManager<*> =
try {
if (isSecureSessionMode) {
// Step 1.1
// The transaction will be certified by the SAM in a secure session.
calypsoExtensionService.createCardTransaction(
calypsoCardApiFactory.createSecureRegularModeTransactionManager(
cardReader, calypsoCard, cardSecuritySettings)
} else {
// Step 1.2
// The transaction will take place without being certified by a SAM
calypsoExtensionService.createCardTransactionWithoutSecurity(cardReader, calypsoCard)
calypsoCardApiFactory.createFreeTransactionManager(cardReader, calypsoCard)
}
} catch (e: Exception) {
// TODO check which condition could lead here
Timber.w(e)
calypsoExtensionService.createCardTransactionWithoutSecurity(cardReader, calypsoCard)
calypsoCardApiFactory.createFreeTransactionManager(cardReader, calypsoCard)
}

if (isSecureSessionMode) {
if (cardTransaction is SecureRegularModeTransactionManager) {
// Open a transaction to read/write the Calypso Card and read the Environment file
cardTransaction.prepareOpenSecureSession(WriteAccessLevel.DEBIT)
}
Expand All @@ -78,7 +81,7 @@ class CardRepository {
// record.
cardTransaction
.prepareReadRecord(CardConstant.SFI_ENVIRONMENT_AND_HOLDER, 1)
.processCommands(false)
.processCommands(ChannelControl.KEEP_OPEN)

val efEnvironmentHolder = calypsoCard.getFileBySfi(CardConstant.SFI_ENVIRONMENT_AND_HOLDER)
val env = EnvironmentHolderStructureParser().parse(efEnvironmentHolder.data.content)
Expand All @@ -87,23 +90,25 @@ class CardRepository {
// the current version) reject the card.
// <Abort Secure Session if any>
if (env.envVersionNumber != VersionNumber.CURRENT_VERSION) {
if (isSecureSessionMode) {
cardTransaction.prepareCancelSecureSession().processCommands(true)
if (cardTransaction is SecureRegularModeTransactionManager) {
cardTransaction.prepareCancelSecureSession().processCommands(ChannelControl.CLOSE_AFTER)
}
throw EnvironmentException("wrong version number")
}

// Step 4 - If EnvEndDate points to a date in the past reject the card.
// <Abort Secure Session if any>
if (env.envEndDate.getDate().isBefore(controlDateTime.toLocalDate())) {
if (isSecureSessionMode) {
cardTransaction.prepareCancelSecureSession().processCommands(true)
if (cardTransaction is SecureRegularModeTransactionManager) {
cardTransaction.prepareCancelSecureSession().processCommands(ChannelControl.CLOSE_AFTER)
}
throw EnvironmentException("End date expired")
}

// Step 5 - Read and unpack the last event record.
cardTransaction.prepareReadRecord(CardConstant.SFI_EVENTS_LOG, 1).processCommands(false)
cardTransaction
.prepareReadRecord(CardConstant.SFI_EVENTS_LOG, 1)
.processCommands(ChannelControl.KEEP_OPEN)

val efEventLog = calypsoCard.getFileBySfi(CardConstant.SFI_EVENTS_LOG)
val event = EventStructureParser().parse(efEventLog.data.content)
Expand All @@ -113,8 +118,8 @@ class CardRepository {
// <Abort Secure Session if any>
val eventVersionNumber = event.eventVersionNumber
if (eventVersionNumber != VersionNumber.CURRENT_VERSION) {
if (isSecureSessionMode) {
cardTransaction.prepareCancelSecureSession().processCommands(true)
if (cardTransaction is SecureRegularModeTransactionManager) {
cardTransaction.prepareCancelSecureSession().processCommands(ChannelControl.CLOSE_AFTER)
}
if (eventVersionNumber == VersionNumber.UNDEFINED) {
throw EventCleanCardException()
Expand Down Expand Up @@ -162,7 +167,7 @@ class CardRepository {
nbContractRecords,
CardConstant.CONTRACT_RECORD_SIZE_BYTES)
.prepareReadCounter(CardConstant.SFI_COUNTERS, nbContractRecords)
.processCommands(false)
.processCommands(ChannelControl.KEEP_OPEN)

val efCounters = calypsoCard.getFileBySfi(CardConstant.SFI_COUNTERS)

Expand Down Expand Up @@ -208,7 +213,7 @@ class CardRepository {
// of the value
// by using the PSO Verify Signature command of the SAM.
@Suppress("ControlFlowWithEmptyBody")
if (isSecureSessionMode && contract.contractAuthenticator != 0) {
if (contract.contractAuthenticator != 0) {
// Step 15.1 - If the value is wrong reject the card.
// <Abort Secure Session if any>
// Step 15.2 - If the value of ContractSaleSam is present in the SAM Black List reject
Expand Down Expand Up @@ -258,8 +263,8 @@ class CardRepository {
status = Status.TICKETS_FOUND

// Step 20 - If isSecureSessionMode is true, Close the session
if (isSecureSessionMode) {
cardTransaction.prepareCloseSecureSession().processCommands(true)
if (cardTransaction is SecureRegularModeTransactionManager) {
cardTransaction.prepareCloseSecureSession().processCommands(ChannelControl.CLOSE_AFTER)
}

var validationList: ArrayList<Validation>? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ import org.calypsonet.keyple.plugin.flowbird.contact.FlowbirdContactReader
import org.calypsonet.keyple.plugin.flowbird.contact.SamSlot
import org.calypsonet.keyple.plugin.flowbird.contactless.FlowbirdContactlessReader
import org.calypsonet.keyple.plugin.flowbird.contactless.FlowbirdSupportContactlessProtocols
import org.calypsonet.terminal.reader.CardReader
import org.calypsonet.terminal.reader.ConfigurableCardReader
import org.calypsonet.terminal.reader.ObservableCardReader
import org.calypsonet.terminal.reader.spi.CardReaderObservationExceptionHandlerSpi
import org.eclipse.keyple.core.service.KeyplePluginException
import org.eclipse.keyple.core.service.Plugin
import org.eclipse.keyple.core.service.SmartCardServiceProvider
import org.eclipse.keyple.plugin.android.nfc.AndroidNfcPlugin
import org.eclipse.keyple.plugin.android.nfc.AndroidNfcPluginFactoryProvider
import org.eclipse.keyple.plugin.android.nfc.AndroidNfcReader
import org.eclipse.keypop.reader.CardReader
import org.eclipse.keypop.reader.ConfigurableCardReader
import org.eclipse.keypop.reader.ObservableCardReader
import org.eclipse.keypop.reader.spi.CardReaderObservationExceptionHandlerSpi

class ReaderRepository
@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import dagger.Module
import dagger.Provides
import org.calypsonet.keyple.demo.control.data.ReaderRepository
import org.calypsonet.keyple.demo.control.di.scope.AppScoped
import org.calypsonet.terminal.reader.spi.CardReaderObservationExceptionHandlerSpi
import org.eclipse.keypop.reader.spi.CardReaderObservationExceptionHandlerSpi
import timber.log.Timber

@Suppress("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,36 @@ import org.calypsonet.keyple.demo.control.data.model.CardReaderResponse
import org.calypsonet.keyple.demo.control.data.model.Location
import org.calypsonet.keyple.demo.control.data.model.ReaderType
import org.calypsonet.keyple.demo.control.di.scope.AppScoped
import org.calypsonet.terminal.calypso.WriteAccessLevel
import org.calypsonet.terminal.calypso.card.CalypsoCard
import org.calypsonet.terminal.calypso.sam.CalypsoSam
import org.calypsonet.terminal.calypso.transaction.CardSecuritySetting
import org.calypsonet.terminal.reader.CardReader
import org.calypsonet.terminal.reader.ObservableCardReader
import org.calypsonet.terminal.reader.selection.CardSelectionManager
import org.calypsonet.terminal.reader.selection.CardSelectionResult
import org.calypsonet.terminal.reader.selection.ScheduledCardSelectionsResponse
import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi
import org.eclipse.keyple.card.calypso.CalypsoExtensionService
import org.eclipse.keyple.card.calypso.crypto.legacysam.LegacySamExtensionService
import org.eclipse.keyple.card.calypso.crypto.legacysam.LegacySamUtil
import org.eclipse.keyple.core.service.KeyplePluginException
import org.eclipse.keyple.core.service.SmartCardServiceProvider
import org.eclipse.keyple.core.util.HexUtil
import org.eclipse.keypop.calypso.card.CalypsoCardApiFactory
import org.eclipse.keypop.calypso.card.WriteAccessLevel
import org.eclipse.keypop.calypso.card.card.CalypsoCard
import org.eclipse.keypop.calypso.card.transaction.SymmetricCryptoSecuritySetting
import org.eclipse.keypop.calypso.crypto.legacysam.sam.LegacySam
import org.eclipse.keypop.reader.CardReader
import org.eclipse.keypop.reader.ObservableCardReader
import org.eclipse.keypop.reader.ReaderApiFactory
import org.eclipse.keypop.reader.selection.CardSelectionManager
import org.eclipse.keypop.reader.selection.CardSelectionResult
import org.eclipse.keypop.reader.selection.ScheduledCardSelectionsResponse
import org.eclipse.keypop.reader.spi.CardReaderObserverSpi
import timber.log.Timber

@AppScoped
class TicketingService @Inject constructor(private var readerRepository: ReaderRepository) {

private val readerApiFactory: ReaderApiFactory =
SmartCardServiceProvider.getService().readerApiFactory
private val calypsoExtensionService: CalypsoExtensionService =
CalypsoExtensionService.getInstance()

private lateinit var calypsoSam: CalypsoSam
private val calypsoCardApiFactory: CalypsoCardApiFactory =
calypsoExtensionService.calypsoCardApiFactory
private lateinit var legacySam: LegacySam
private lateinit var calypsoCard: CalypsoCard
private lateinit var cardSelectionManager: CardSelectionManager

Expand Down Expand Up @@ -135,44 +142,47 @@ class TicketingService @Inject constructor(private var readerRepository: ReaderR
smartCardService.checkCardExtension(calypsoExtensionService)

// Get a new card selection manager
cardSelectionManager = smartCardService.createCardSelectionManager()
cardSelectionManager = readerApiFactory.createCardSelectionManager()

// Prepare card selection case #1: Keyple generic
indexOfKeypleGenericCardSelection =
cardSelectionManager.prepareSelection(
calypsoExtensionService
.createCardSelection()
readerApiFactory
.createIsoCardSelector()
.filterByDfName(CardConstant.AID_KEYPLE_GENERIC)
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()))
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()),
calypsoCardApiFactory.createCalypsoCardSelectionExtension())

// Prepare card selection case #2: CD LIGHT/GTML
indexOfCdLightGtmlCardSelection =
cardSelectionManager.prepareSelection(
calypsoExtensionService
.createCardSelection()
readerApiFactory
.createIsoCardSelector()
.filterByDfName(CardConstant.AID_CD_LIGHT_GTML)
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()))
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()),
calypsoCardApiFactory.createCalypsoCardSelectionExtension())

// Prepare card selection case #3: CALYPSO LIGHT
indexOfCalypsoLightCardSelection =
cardSelectionManager.prepareSelection(
calypsoExtensionService
.createCardSelection()
readerApiFactory
.createIsoCardSelector()
.filterByDfName(CardConstant.AID_CALYPSO_LIGHT)
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()))
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()),
calypsoCardApiFactory.createCalypsoCardSelectionExtension())

// Prepare card selection case #4: Navigo IDF
indexOfNavigoIdfCardSelection =
cardSelectionManager.prepareSelection(
calypsoExtensionService
.createCardSelection()
readerApiFactory
.createIsoCardSelector()
.filterByDfName(CardConstant.AID_NORMALIZED_IDF)
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()))
.filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName()),
calypsoCardApiFactory.createCalypsoCardSelectionExtension())

// Schedule the execution of the prepared card selection scenario as soon as a card is presented
cardSelectionManager.scheduleCardSelectionScenario(
readerRepository.getCardReader() as ObservableCardReader,
ObservableCardReader.DetectionMode.REPEATING,
ObservableCardReader.NotificationMode.ALWAYS)
}

Expand Down Expand Up @@ -216,10 +226,13 @@ class TicketingService @Inject constructor(private var readerRepository: ReaderR
controlDateTime = LocalDateTime.now())
}

private fun getSecuritySettings(): CardSecuritySetting? {
return calypsoExtensionService
.createCardSecuritySetting()
.setControlSamResource(readerRepository.getSamReader(), calypsoSam)
private fun getSecuritySettings(): SymmetricCryptoSecuritySetting? {
return calypsoCardApiFactory
.createSymmetricCryptoSecuritySetting(
LegacySamExtensionService.getInstance()
.getLegacySamApiFactory()
.createSymmetricCryptoCardTransactionManagerFactory(
readerRepository.getSamReader(), legacySam))
.assignDefaultKif(
WriteAccessLevel.PERSONALIZATION, CardConstant.DEFAULT_KIF_PERSONALIZATION)
.assignDefaultKif(WriteAccessLevel.LOAD, CardConstant.DEFAULT_KIF_LOAD)
Expand All @@ -228,23 +241,25 @@ class TicketingService @Inject constructor(private var readerRepository: ReaderR
}

private fun selectSam(samReader: CardReader): Boolean {
// Get the Keyple main service
val smartCardService = SmartCardServiceProvider.getService()

// Create a SAM selection manager.
val samSelectionManager: CardSelectionManager = smartCardService.createCardSelectionManager()
val samSelectionManager: CardSelectionManager = readerApiFactory.createCardSelectionManager()

// Create a SAM selection using the Calypso card extension.
samSelectionManager.prepareSelection(
calypsoExtensionService
.createSamSelection()
.filterByProductType(CalypsoSam.ProductType.SAM_C1))
readerApiFactory
.createBasicCardSelector()
.filterByPowerOnData(
LegacySamUtil.buildPowerOnDataFilter(LegacySam.ProductType.SAM_C1, null)),
LegacySamExtensionService.getInstance()
.legacySamApiFactory
.createLegacySamSelectionExtension())
try {
// SAM communication: run the selection scenario.
val samSelectionResult = samSelectionManager.processCardSelectionScenario(samReader)

// Get the Calypso SAM SmartCard resulting of the selection.
calypsoSam = samSelectionResult.activeSmartCard!! as CalypsoSam
legacySam = samSelectionResult.activeSmartCard!! as LegacySam
return true
} catch (e: Exception) {
Timber.e(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import org.calypsonet.keyple.demo.control.data.model.CardReaderResponse
import org.calypsonet.keyple.demo.control.data.model.Status
import org.calypsonet.keyple.demo.control.di.scope.ActivityScoped
import org.calypsonet.keyple.demo.control.ui.cardcontent.CardContentActivity
import org.calypsonet.terminal.reader.CardReaderEvent
import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi
import org.eclipse.keypop.reader.CardReaderEvent
import org.eclipse.keypop.reader.spi.CardReaderObserverSpi
import timber.log.Timber

@ActivityScoped
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 2023.06.01
version = 2023.12.04
archivesBaseName = keyple-android-demo-control

# Project-wide Gradle settings.
Expand Down

0 comments on commit 70a82e4

Please sign in to comment.