Skip to content

Commit

Permalink
Merge branch '2.3.8' into 'master'
Browse files Browse the repository at this point in the history
2.3.8

See merge request oec/open-ecard!88
  • Loading branch information
florian-otto committed Feb 4, 2025
2 parents 307438f + c73fa74 commit 2ead4e4
Show file tree
Hide file tree
Showing 21 changed files with 539 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class CardLinkProcess(

// no error means success
val bindingResult = BindingResult(BindingResultCode.OK)
bindingResult.addParameter(CardLinkKeys.PERSONAL_DATA, dynCtx.get(CardLinkKeys.PERSONAL_DATA) as String)
bindingResult.addParameter(CardLinkKeys.CARD_SESSION_ID, cardSessionId)
bindingResult.addParameter(CardLinkKeys.ICCSN, iccsn)
iccsnReassignment?.let { bindingResult.addParameter(CardLinkKeys.ICCSN_REASSIGNMENT_TIMESTAMP, it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@ object CardLinkKeys {
const val ERROR_MESSAGE = "${prefix}ERROR_MESSAGE"
const val ICCSN_REASSIGNMENT_TIMESTAMP = "${prefix}ICCSN_REASSIGNMENT_TIMESTAMP"
const val ERROR_CODE = "${prefix}ERROR_CODE"
const val PERSONAL_DATA = "${prefix}PERSONAL_DATA"
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,15 @@ import org.openecard.gui.ResultStatus
import org.openecard.gui.UserConsentNavigator
import org.openecard.gui.executor.ExecutionEngine
import org.openecard.mobile.activation.CardLinkErrorCodes
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.util.*
import java.util.zip.GZIPInputStream
import javax.management.Query.and
import kotlin.experimental.and

private val logger = KotlinLogging.logger {}

Expand Down Expand Up @@ -87,6 +95,8 @@ class CardLinkStep(val aCtx: Context) : ProtocolStep<DIDAuthenticate, DIDAuthent
val cardSessionId = dynCtx.get(CardLinkKeys.CARD_SESSION_ID) as String
val conHandle = dynCtx.get(TR03112Keys.CONNECTION_HANDLE) as ConnectionHandleType

readPersonalInformation(conHandle, cardSessionId, dynCtx)

val egkData = readEgkData(conHandle, cardSessionId, dynCtx)
sendEgkData(egkData, cardSessionId, ws)

Expand All @@ -95,6 +105,29 @@ class CardLinkStep(val aCtx: Context) : ProtocolStep<DIDAuthenticate, DIDAuthent
}
}

@OptIn(ExperimentalStdlibApi::class)
private fun readPersonalInformation(conHandle: ConnectionHandleType, cardSessionId: String, dynCtx: DynamicContext) {
val infos = DidInfos(aCtx.dispatcher, null, conHandle)
val efPd_dataset = infos.getDataSetInfo("EF.PD").read()

val lengthPD = ByteBuffer.wrap(efPd_dataset, 0, 2).asShortBuffer().get()
val rawPersonalData = ByteBuffer.wrap(efPd_dataset, 2, lengthPD.toInt())

val bos = ByteArrayOutputStream()

GZIPInputStream(object : InputStream() {
override fun read(): Int {
return if(rawPersonalData.hasRemaining()){
return rawPersonalData.get().toUByte().toInt()
} else {
-1
}
}
}).transferTo(bos)

dynCtx.put(CardLinkKeys.PERSONAL_DATA, bos.toByteArray().toHexString())
}

private fun readEgkData(conHandle: ConnectionHandleType, cardSessionId: String, dynCtx: DynamicContext): RegisterEgk {
val infos = DidInfos(aCtx.dispatcher, null, conHandle)
val gdoDs = infos.getDataSetInfo("EF.GDO").read()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ package org.openecard.addons.cardlink.sal.gui
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType
import org.openecard.addon.Context
import org.openecard.addons.cardlink.ws.WsPair
import org.openecard.common.util.SysUtils
import org.openecard.gui.definition.UserConsentDescription
import org.openecard.mobile.activation.Websocket

Expand All @@ -38,7 +39,14 @@ class CardLinkUserConsent(ws: WsPair, addonCtx: Context, isPhoneRegistered: Bool
add(PhoneStep(ws))
add(TanStep(ws))
}
add(EnterCanStep(ws, addonCtx, sessionHandle))
//on mobile we definitely have NFC so we will need a CAN
if(SysUtils.isMobileDevice()) {
add(EnterCanStep(ws, addonCtx, sessionHandle))
//desktop might have a contact based reader allowing to connect without can
//if direct connect fails, EnterCanStep will be added by DirectConnectStep
} else {
add(DirectConnectStep(ws, addonCtx, sessionHandle))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/****************************************************************************
* Copyright (C) 2024 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH ([email protected])
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/

package org.openecard.addons.cardlink.sal.gui

import io.github.oshai.kotlinlogging.KotlinLogging
import iso.std.iso_iec._24727.tech.schema.*
import org.openecard.addon.Context
import org.openecard.addons.cardlink.sal.CardLinkKeys
import org.openecard.addons.cardlink.ws.WsPair
import org.openecard.binding.tctoken.TR03112Keys
import org.openecard.common.DynamicContext
import org.openecard.common.ECardConstants
import org.openecard.common.I18n
import org.openecard.common.WSHelper.WSException
import org.openecard.common.WSHelper.checkResult
import org.openecard.common.sal.util.InsertCardHelper
import org.openecard.gui.StepResult
import org.openecard.gui.StepWithConnection
import org.openecard.gui.executor.ExecutionResults
import org.openecard.gui.executor.StepAction
import org.openecard.gui.executor.StepActionResult
import org.openecard.gui.executor.StepActionResultStatus
import org.openecard.mobile.activation.CardLinkErrorCodes
import org.openecard.sal.protocol.eac.gui.ErrorStep

private val LOG = KotlinLogging.logger {}

private const val STEP_ID = "PROTOCOL_CARDLINK_GUI_STEP_DIRECT_CONNECT"
private const val STEP_TITLE = "Direct connect"

private val lang = I18n.getTranslation("pace")
private val langPin = I18n.getTranslation("pinplugin")

private const val ERROR_CARD_REMOVED = "action.error.card.removed"
private const val ERROR_TITLE = "action.error.title"
private const val ERROR_UNKNOWN = "action.error.unknown"

class DirectConnectStep(
val ws: WsPair,
val addonCtx: Context,
val sessHandle: ConnectionHandleType,
stepId: String = STEP_ID,
title: String = STEP_TITLE
) : StepWithConnection(stepId, title, sessHandle) {
init {
setAction(DirectConnectStepAction(this))
}
}

class DirectConnectStepAction(private val directConnectStep: DirectConnectStep) : StepAction(directConnectStep) {

override fun perform(oldResults: MutableMap<String, ExecutionResults>, result: StepResult): StepActionResult {
val dynCtx = DynamicContext.getInstance(TR03112Keys.INSTANCE_KEY)

try {
val requiredCardTypes = setOf("http://ws.gematik.de/egk/1.0.0")
val conHandle =
dynCtx.get(TR03112Keys.CONNECTION_HANDLE) as ConnectionHandleType? ?: directConnectStep.sessHandle
val ph = InsertCardHelper(directConnectStep.addonCtx, conHandle)
// connect card as we need to use it right away
val cardHandle = ph.connectCardIfNeeded(requiredCardTypes)
// safe for later process
dynCtx.put(TR03112Keys.CONNECTION_HANDLE, cardHandle)

//if we have a wired card we can procede with cardlink
return if(isWired(cardHandle, dynCtx)){
StepActionResult(StepActionResultStatus.NEXT)
//we have radio based connection, we will net CAN
} else {
StepActionResult(
StepActionResultStatus.REPEAT,
EnterCanStep(
directConnectStep.ws,
directConnectStep.addonCtx,
directConnectStep.sessHandle
)
)
}


} catch (ex: WSException) {
// for people which think they have to remove the card in the process
if (ex.resultMinor == ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE) {
val errorMessage = "The SlotHandle was invalid so probably the user removed the card or an reset occurred."
LOG.error(ex) { errorMessage }

dynCtx.put(CardLinkKeys.CLIENT_ERROR_CODE, CardLinkErrorCodes.ClientCodes.INVALID_SLOT_HANDLE)
dynCtx.put(CardLinkKeys.ERROR_MESSAGE, errorMessage)

return StepActionResult(
StepActionResultStatus.REPEAT,
ErrorStep(
lang.translationForKey(ERROR_TITLE),
langPin.translationForKey(ERROR_CARD_REMOVED), ex
)
)
}

val errorMessage = "An unknown error occurred."
LOG.error(ex) { errorMessage }

dynCtx.put(CardLinkKeys.CLIENT_ERROR_CODE, CardLinkErrorCodes.ClientCodes.OTHER_PACE_ERROR)
dynCtx.put(CardLinkKeys.ERROR_MESSAGE, errorMessage)

return StepActionResult(
StepActionResultStatus.CANCEL,
ErrorStep(
langPin.translationForKey(ERROR_TITLE),
langPin.translationForKey(ERROR_UNKNOWN), ex
)
)
} catch (ex: InterruptedException) {
val errorMessage = "Connect step action interrupted."
LOG.info { errorMessage }

dynCtx.put(CardLinkKeys.CLIENT_ERROR_CODE, CardLinkErrorCodes.ClientCodes.CAN_STEP_INTERRUPTED)
dynCtx.put(CardLinkKeys.ERROR_MESSAGE, errorMessage)

return StepActionResult(StepActionResultStatus.REPEAT)
}
}

@Throws(WSException::class)
private fun isWired(conHandle: ConnectionHandleType, dynCtx: DynamicContext): Boolean {
val req = GetIFDCapabilities().apply {
contextHandle = conHandle.contextHandle
ifdName = conHandle.ifdName
}

val capabilitiesResponse = directConnectStep.addonCtx.dispatcher.safeDeliver(req) as GetIFDCapabilitiesResponse
checkResult(capabilitiesResponse)

val isWired = capabilitiesResponse.ifdCapabilities.slotCapability
.flatMap{ it.protocol }
.firstOrNull { p -> ECardConstants.IFD.Protocol.isWired(p) }

return isWired != null

}
}
30 changes: 30 additions & 0 deletions clients/desktop-lib/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
description = "desktop-lib"

plugins {
id("openecard.lib-multiplatform-conventions")
}

kotlin {

sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.kotlin.logging)
}
}
val commonTest by getting {
dependencies {
implementation(libs.bundles.test.basics.kotlin)
}
}
val jvmMain by getting {
dependencies {
api(project(":clients:mobile-lib"))
api(project(":ifd:scio-backend:pcsc"))
api(project(":addons:cardlink"))
api(project(":wsdef:jaxb-marshaller"))
implementation(libs.xerces.imp)
}
}
}
}
Loading

0 comments on commit 2ead4e4

Please sign in to comment.