@ledgerhq/device-transport-kit-react-native-hid 0.0.0-rn-hid-20250221112139 → 0.0.0-rnhid-transport-20250411151739

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +101 -0
  3. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  4. package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  5. package/android/gradle.properties +1 -0
  6. package/android/gradlew +252 -0
  7. package/android/gradlew.bat +94 -0
  8. package/android/src/main/AndroidManifest.xml +3 -0
  9. package/android/src/main/kotlin/com/ledger/androidtransporthid/BridgeEvents.kt +42 -0
  10. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidModule.kt +241 -0
  11. package/android/src/main/kotlin/com/ledger/androidtransporthid/TransportHidPackage.kt +25 -0
  12. package/android/src/main/kotlin/com/ledger/androidtransporthid/bridge/serialization.kt +124 -0
  13. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/AndroidUsbTransport.kt +16 -0
  14. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/DefaultAndroidUsbTransport.kt +298 -0
  15. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/UsbPermissionRequester.kt +18 -0
  16. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/connection/AndroidUsbApduSender.kt +133 -0
  17. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbAttachedReceiverController.kt +59 -0
  18. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbDetachedReceiverController.kt +58 -0
  19. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/controller/UsbPermissionReceiver.kt +92 -0
  20. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/LedgerUsbDevice.kt +16 -0
  21. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/ProductId.kt +11 -0
  22. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbPermissionEvent.kt +14 -0
  23. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/UsbState.kt +16 -0
  24. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/model/VendorId.kt +11 -0
  25. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbDeviceMapper.kt +46 -0
  26. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMain/transport/usb/utils/UsbMapper.kt +56 -0
  27. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/UsbConst.android.kt +8 -0
  28. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceApduSender.kt +13 -0
  29. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnection.kt +95 -0
  30. package/android/src/main/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachine.kt +314 -0
  31. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/Apdu.kt +44 -0
  32. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduBuilder.kt +88 -0
  33. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduParser.kt +37 -0
  34. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/ApduUtils.kt +37 -0
  35. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/apdu/SendApduResult.kt +47 -0
  36. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectedDevice.kt +25 -0
  37. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/connection/ConnectionResult.kt +45 -0
  38. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/BleInformation.kt +8 -0
  39. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/LedgerDevice.kt +89 -0
  40. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/device/UsbInfo.kt +7 -0
  41. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/disconnection/DisconnectionResult.kt +10 -0
  42. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/ConnectivityType.kt +10 -0
  43. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryDevice.kt +18 -0
  44. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/discovery/DiscoveryResult.kt +28 -0
  45. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/ByteArrayExtension.kt +116 -0
  46. package/android/src/main/kotlin/com/ledger/devicesdk/shared/api/utils/StringExtension.kt +21 -0
  47. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectedDevice.kt +13 -0
  48. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/connection/InternalConnectionResult.kt +41 -0
  49. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScope.kt +25 -0
  50. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/coroutine/SDKScopeHandler.kt +18 -0
  51. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/event/SdkEventDispatcher.kt +19 -0
  52. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/DisableLoggerService.kt +12 -0
  53. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogInfo.kt +52 -0
  54. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LogLevel.kt +13 -0
  55. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/service/logger/LoggerService.kt +10 -0
  56. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/Transport.kt +21 -0
  57. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/TransportEvent.kt +18 -0
  58. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerService.kt +210 -0
  59. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/FramerUtils.kt +35 -0
  60. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduConst.kt +9 -0
  61. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFrame.kt +66 -0
  62. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/ApduFramerHeader.kt +74 -0
  63. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/framer/model/FramerConst.kt +14 -0
  64. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/ByteExtension.kt +21 -0
  65. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/transport/utils/InternalByteArrayExtension.kt +18 -0
  66. package/android/src/main/kotlin/com/ledger/devicesdk/shared/internal/utils/Controller.kt +12 -0
  67. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionStateMachineTest.kt +713 -0
  68. package/android/src/test/kotlin/com/ledger/devicesdk/shared/androidMainInternal/transport/deviceconnection/DeviceConnectionTest.kt +218 -0
  69. package/lib/cjs/package.json +2 -1
  70. package/lib/esm/package.json +2 -1
  71. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  72. package/package.json +6 -5
@@ -0,0 +1,18 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.coroutine
7
+
8
+ import kotlinx.coroutines.Dispatchers
9
+ import kotlinx.coroutines.SupervisorJob
10
+
11
+ private lateinit var scope: SdkCloseableScope
12
+
13
+ internal fun getSdkScope(): SdkCloseableScope {
14
+ if (!::scope.isInitialized) {
15
+ scope = SdkCloseableScope(context = Dispatchers.Default + SupervisorJob())
16
+ }
17
+ return scope
18
+ }
@@ -0,0 +1,19 @@
1
+ package com.ledger.devicesdk.shared.internal.event
2
+
3
+ import com.ledger.devicesdk.shared.internal.coroutine.sdkScope
4
+ import com.ledger.devicesdk.shared.internal.transport.TransportEvent
5
+ import kotlinx.coroutines.flow.Flow
6
+ import kotlinx.coroutines.flow.MutableSharedFlow
7
+ import kotlinx.coroutines.launch
8
+
9
+ internal class SdkEventDispatcher {
10
+ private val eventFlow: MutableSharedFlow<TransportEvent> = MutableSharedFlow()
11
+
12
+ fun listen(): Flow<TransportEvent> = eventFlow
13
+
14
+ fun dispatch(event: TransportEvent) {
15
+ sdkScope.launch {
16
+ eventFlow.emit(value = event)
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.service.logger
7
+
8
+ internal class DisableLoggerService : LoggerService {
9
+ override fun log(info: LogInfo) {
10
+ //DO NOTING
11
+ }
12
+ }
@@ -0,0 +1,52 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.service.logger
7
+
8
+ internal data class LogInfo(
9
+ val level: LogLevel,
10
+ val tag: String,
11
+ val message: String,
12
+ val jsonPayLoad: Map<String, String>,
13
+ )
14
+
15
+ internal fun buildSimpleDebugLogInfo(
16
+ tag: String,
17
+ message: String,
18
+ ): LogInfo =
19
+ LogInfo(level = LogLevel.DEBUG, tag = tag, message = message, emptyMap())
20
+
21
+ internal fun buildSimpleErrorLogInfo(
22
+ tag: String,
23
+ message: String,
24
+ ): LogInfo =
25
+ LogInfo(level = LogLevel.ERROR, tag = tag, message = message, emptyMap())
26
+
27
+ internal fun buildSimpleWarningLogInfo(
28
+ tag: String,
29
+ message: String,
30
+ ): LogInfo =
31
+ LogInfo(level = LogLevel.WARNING, tag = tag, message = message, emptyMap())
32
+
33
+ internal fun buildSimpleInfoLogInfo(
34
+ tag: String,
35
+ message: String,
36
+ ): LogInfo =
37
+ LogInfo(level = LogLevel.INFO, tag = tag, message = message, emptyMap())
38
+
39
+ private fun buildSimpleLogInfo(
40
+ level: LogLevel,
41
+ tag: String,
42
+ message: String,
43
+ ): LogInfo =
44
+ LogInfo(level = level, tag = tag, message = message, emptyMap())
45
+
46
+ internal fun buildComplexLogInfo(
47
+ level: LogLevel,
48
+ tag: String,
49
+ message: String,
50
+ jsonPayLoad: Map<String, String>,
51
+ ): LogInfo =
52
+ LogInfo(level = level, tag = tag, message = message, jsonPayLoad = jsonPayLoad)
@@ -0,0 +1,13 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.service.logger
7
+
8
+ internal enum class LogLevel {
9
+ DEBUG,
10
+ INFO,
11
+ WARNING,
12
+ ERROR
13
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.service.logger
7
+
8
+ internal fun interface LoggerService {
9
+ fun log(info: LogInfo)
10
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport
7
+
8
+ import com.ledger.devicesdk.shared.api.discovery.DiscoveryDevice
9
+ import com.ledger.devicesdk.shared.internal.connection.InternalConnectionResult
10
+ import kotlinx.coroutines.flow.Flow
11
+
12
+ internal interface Transport {
13
+ fun startScan(): Flow<List<DiscoveryDevice>>
14
+
15
+ fun stopScan()
16
+
17
+ // TODO change by Flow<ConnectedDeviceState> or add observe device connection for listening device state flow through?
18
+ suspend fun connect(discoveryDevice: DiscoveryDevice): InternalConnectionResult
19
+
20
+ suspend fun disconnect(deviceId: String)
21
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport
7
+
8
+ internal sealed class TransportEvent {
9
+ data class DeviceConnectionLost(
10
+ val id: String,
11
+ ) : TransportEvent()
12
+ }
13
+
14
+ internal sealed class BluetoothTransportEvent : TransportEvent() {
15
+ data object BluetoothDisable : BluetoothTransportEvent()
16
+
17
+ data object BluetoothEnable : BluetoothTransportEvent()
18
+ }
@@ -0,0 +1,210 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2025 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer
7
+
8
+ import com.ledger.devicesdk.shared.api.apdu.ApduParser
9
+ import com.ledger.devicesdk.shared.api.utils.toHexadecimalString
10
+ import com.ledger.devicesdk.shared.internal.service.logger.LoggerService
11
+ import com.ledger.devicesdk.shared.internal.service.logger.buildSimpleInfoLogInfo
12
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.ApduFrame
13
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.ApduFramerHeader
14
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.HEADER_SIZE
15
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.MAXIMUM_HEADER_SIZE
16
+ import com.ledger.devicesdk.shared.internal.transport.utils.extractApduSize
17
+ import com.ledger.devicesdk.shared.internal.transport.utils.extractFrameHeader
18
+
19
+ private const val TAG = "FramerService"
20
+
21
+ internal class FramerService(
22
+ private val loggerService: LoggerService
23
+ ) {
24
+ fun serialize(
25
+ mtu: Int,
26
+ channelId: ByteArray?,
27
+ rawApdu: ByteArray,
28
+ ): List<ApduFrame> {
29
+ val header = ApduFramerHeader(channelId = channelId, frameId = byteArrayOf(0x00, 0x00))
30
+ val apduSize = rawApdu.size.to2BytesArray()
31
+ val frames =
32
+ if (rawApdu.isShortApdu(mtu = mtu)) {
33
+ val paddingSize = mtu - header.size() - apduSize.size - rawApdu.size
34
+ val finalApdu = rawApdu + ByteArray(paddingSize)
35
+ listOf(
36
+ ApduFrame(
37
+ header = header,
38
+ apduSize = apduSize,
39
+ apdu = finalApdu,
40
+ ),
41
+ )
42
+ } else {
43
+ var isBuildingFrames = true
44
+ var counter = 0
45
+ var startIndex = 0
46
+ var endIndex = mtu - header.size() - apduSize.size
47
+ buildList {
48
+ while (isBuildingFrames) {
49
+ if (counter == 0) {
50
+ // loggerService.log(
51
+ // info = buildSimpleInfoLogInfo(
52
+ // tag = TAG,
53
+ // message = "-- IF -- startIndex = $startIndex / endIndex = $endIndex",
54
+ // ),
55
+ // )
56
+ add(
57
+ ApduFrame(
58
+ header = header,
59
+ apduSize = apduSize,
60
+ apdu = rawApdu.slice(startIndex..<endIndex).toByteArray(),
61
+ ),
62
+ )
63
+ } else {
64
+ startIndex = endIndex
65
+ endIndex = (mtu - header.size()) + startIndex
66
+ // loggerService.log(
67
+ // info = buildSimpleInfoLogInfo(
68
+ // tag = TAG,
69
+ // message = "-- ELSE -- startIndex = $startIndex / endIndex = $endIndex",
70
+ // ),
71
+ // )
72
+ if (endIndex <= rawApdu.size) {
73
+ add(
74
+ ApduFrame(
75
+ ApduFramerHeader(
76
+ channelId = channelId,
77
+ frameId = counter.to2BytesArray(),
78
+ ),
79
+ apduSize = null,
80
+ apdu = rawApdu.slice(startIndex..<endIndex).toByteArray(),
81
+ ),
82
+ )
83
+ } else {
84
+ val apduExtracted = rawApdu.slice(startIndex..<rawApdu.size).toByteArray()
85
+ val paddingSize = mtu - header.size() - apduExtracted.size
86
+ val finalApdu =
87
+ apduExtracted + ByteArray(paddingSize)
88
+ add(
89
+ ApduFrame(
90
+ header =
91
+ ApduFramerHeader(
92
+ channelId = channelId,
93
+ frameId = counter.to2BytesArray(),
94
+ ),
95
+ apduSize = null,
96
+ apdu = finalApdu,
97
+ ),
98
+ )
99
+ isBuildingFrames = false
100
+ }
101
+ }
102
+ counter += 1
103
+ }
104
+ }
105
+ }
106
+ // loggerService.log(
107
+ // info = buildSimpleInfoLogInfo(
108
+ // tag = TAG,
109
+ // message = "APDU SERIALIZATION RESULT : \n${frames.toHexadecimalString()}",
110
+ // ),
111
+ // )
112
+ return frames
113
+ }
114
+
115
+ fun deserialize(
116
+ mtu: Int,
117
+ frames: List<ApduFrame>,
118
+ ): ByteArray {
119
+ // loggerService.log(
120
+ // info = buildSimpleInfoLogInfo(
121
+ // tag = TAG,
122
+ // message = "APDU DESERIALIZATION RESULT : \n",
123
+ // ),
124
+ // )
125
+ var payload = byteArrayOf()
126
+ return if (frames.isEmpty()) {
127
+ payload
128
+ } else {
129
+ var rawApdu: ByteArray
130
+ var offset = mtu - MAXIMUM_HEADER_SIZE
131
+ var apduSize = frames.first().apduSize!!.toIntOn2Bytes()
132
+ // loggerService.log(
133
+ // info = buildSimpleInfoLogInfo(
134
+ // tag = TAG,
135
+ // message =
136
+ // "Header: ${frames.first().header}\n" +
137
+ // "ApduSize : $apduSize",
138
+ // ),
139
+ // )
140
+ for (apduFrame in frames) {
141
+ if (offset < apduSize) {
142
+ rawApdu = apduFrame.apdu.extractApdu(toExclusive = offset)
143
+ payload += rawApdu
144
+ apduSize -= offset
145
+ offset = mtu - HEADER_SIZE
146
+ } else {
147
+ rawApdu = apduFrame.apdu.extractApdu(toExclusive = apduSize)
148
+ payload += rawApdu
149
+ break
150
+ }
151
+ // loggerService.log(
152
+ // info = buildSimpleInfoLogInfo(
153
+ // tag = TAG,
154
+ // message = "Apdu : ${rawApdu.toHexadecimalString()}",
155
+ // ),
156
+ // )
157
+ }
158
+ return payload
159
+ }
160
+ }
161
+
162
+ fun createApduFrames(
163
+ mtu: Int,
164
+ isUsbTransport: Boolean,
165
+ onCreateBuffer: ()->ByteArray,
166
+ ): List<ApduFrame> {
167
+ var firstFrame = true
168
+ var nbrDataRead = 0
169
+ var apduSizeInt = 0
170
+ return buildList {
171
+ do {
172
+ val rawApdu: ByteArray
173
+ val apduSize: ByteArray?
174
+ val buffer = onCreateBuffer()
175
+ if(buffer.isEmpty()){
176
+ return emptyList()
177
+ }
178
+ val parser = ApduParser(response = buffer)
179
+ val header = buffer.extractFrameHeader(isUsbTransport = isUsbTransport, parser = parser)
180
+ if (firstFrame) {
181
+ apduSize = buffer.extractApduSize(parser = parser)
182
+ apduSizeInt = apduSize.toIntOn2Bytes()
183
+ rawApdu = parser.extractRemainingBytesValue()
184
+ firstFrame = false
185
+ } else {
186
+ apduSize = null
187
+ rawApdu = parser.extractRemainingBytesValue()
188
+ }
189
+ nbrDataRead += mtu
190
+ add(
191
+ ApduFrame(
192
+ header = header,
193
+ apduSize = apduSize,
194
+ apdu = rawApdu,
195
+ ),
196
+ )
197
+ // loggerService.log(
198
+ // buildSimpleInfoLogInfo(
199
+ // tag = TAG,
200
+ // message = "APDU received = ${rawApdu.toHexadecimalString()}",
201
+ // ),
202
+ // )
203
+ } while (nbrDataRead < apduSizeInt)
204
+ }
205
+ }
206
+
207
+ private fun ByteArray.isShortApdu(mtu: Int): Boolean = this.size < mtu - MAXIMUM_HEADER_SIZE
208
+
209
+ private fun ByteArray.extractApdu(toExclusive: Int) = this.sliceArray(0..<toExclusive)
210
+ }
@@ -0,0 +1,35 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer
7
+
8
+ import com.ditchoom.buffer.ByteOrder
9
+ import com.ditchoom.buffer.PlatformBuffer
10
+ import com.ditchoom.buffer.allocate
11
+ import com.ditchoom.buffer.wrap
12
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.ApduFrame
13
+
14
+ internal fun Int.to2BytesArray(): ByteArray {
15
+ val result = PlatformBuffer.allocate(Int.SIZE_BYTES).apply { writeShort(this@to2BytesArray.toShort()) }
16
+
17
+ return byteArrayOf(result[0], result[1])
18
+ }
19
+
20
+ internal fun List<ApduFrame>.toHexadecimalString(): String {
21
+ var result = ""
22
+ forEach { result += it.toString() }
23
+ return result
24
+ }
25
+
26
+ internal fun ByteArray.toIntOn2Bytes(): Int =
27
+ PlatformBuffer.wrap(this).readShort().toInt()
28
+
29
+ internal fun ByteArray.toUInt(bo: ByteOrder = ByteOrder.LITTLE_ENDIAN) = this.toInt(bo = bo).toUInt()
30
+
31
+ internal fun ByteArray.toInt(bo: ByteOrder = ByteOrder.LITTLE_ENDIAN): Int {
32
+ val paddedByteArray = ByteArray(Int.SIZE_BYTES)
33
+ this.copyInto(paddedByteArray)
34
+ return PlatformBuffer.wrap(array = paddedByteArray, byteOrder = bo).readInt()
35
+ }
@@ -0,0 +1,9 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer.model
7
+
8
+ internal const val MINIMUM_APDU_SIZE = 4
9
+ internal const val APDU_SIZE_SIZE = 2
@@ -0,0 +1,66 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer.model
7
+
8
+ @OptIn(ExperimentalStdlibApi::class) // Use of Byte.toHexString()
9
+ internal data class ApduFrame(
10
+ val header: ApduFramerHeader,
11
+ val apduSize: ByteArray?,
12
+ val apdu: ByteArray,
13
+ ) {
14
+ init {
15
+ apduSize?.let { require(it.size == APDU_SIZE_SIZE) }
16
+ }
17
+
18
+ fun toByteArray(): ByteArray =
19
+ header.toByteArray() +
20
+ (apduSize?.let { byteArrayOf(apduSize[0], apduSize[1]) } ?: byteArrayOf()) +
21
+ apdu
22
+
23
+ fun size(): Int {
24
+ val apduSize = apduSize?.size ?: 0
25
+ return header.size() +
26
+ apduSize +
27
+ apdu.size
28
+ }
29
+
30
+ override fun toString(): String {
31
+ var result = "$header\napduSize = "
32
+ apduSize?.map {
33
+ result += "${it.toHexString().uppercase()} "
34
+ }
35
+ result += "\napdu = "
36
+ apdu.map {
37
+ result += "${it.toHexString().uppercase()} "
38
+ }
39
+ return result
40
+ }
41
+
42
+ override fun equals(other: Any?): Boolean {
43
+ if (this === other) return true
44
+ if (other == null || this::class != other::class) return false
45
+
46
+ other as ApduFrame
47
+
48
+ if (header != other.header) return false
49
+ if (apduSize != null) {
50
+ if (other.apduSize == null) return false
51
+ if (!apduSize.contentEquals(other.apduSize)) return false
52
+ } else if (other.apduSize != null) {
53
+ return false
54
+ }
55
+ if (!apdu.contentEquals(other.apdu)) return false
56
+
57
+ return true
58
+ }
59
+
60
+ override fun hashCode(): Int {
61
+ var result = header.hashCode()
62
+ result = 31 * result + (apduSize?.contentHashCode() ?: 0)
63
+ result = 31 * result + apdu.contentHashCode()
64
+ return result
65
+ }
66
+ }
@@ -0,0 +1,74 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer.model
7
+
8
+ @OptIn(ExperimentalStdlibApi::class)
9
+ internal data class ApduFramerHeader(
10
+ val channelId: ByteArray?,
11
+ val tagId: Byte = 0x05.toByte(),
12
+ val frameId: ByteArray,
13
+ ) {
14
+ init {
15
+ if(channelId != null){
16
+ require(channelId.size == CHANNEL_ID_SIZE)
17
+ }
18
+ require(frameId.size == FRAME_ID_SIZE)
19
+ }
20
+
21
+ fun toByteArray(): ByteArray {
22
+ return if(channelId != null){
23
+ channelId +
24
+ tagId +
25
+ frameId
26
+ }
27
+ else{
28
+ byteArrayOf(tagId) + frameId
29
+ }
30
+ }
31
+
32
+ override fun toString(): String {
33
+ var result = "--HEADER FRAME--\nchannelId = "
34
+ channelId?.map {
35
+ result += "${it.toHexString().uppercase()} "
36
+ }
37
+ result += "\ntagId = ${tagId.toHexString().uppercase()}"
38
+ result += "\nframeId = "
39
+ frameId.map {
40
+ result += "${it.toHexString().uppercase()} "
41
+ }
42
+ return result
43
+ }
44
+
45
+ fun size(): Int {
46
+ val size = frameId.size + 1 // 1 byte relating to the tagId
47
+ return if(channelId != null){
48
+ size + channelId.size
49
+ }
50
+ else{
51
+ size
52
+ }
53
+ }
54
+
55
+ override fun equals(other: Any?): Boolean {
56
+ if (this === other) return true
57
+ if (other == null || this::class != other::class) return false
58
+
59
+ other as ApduFramerHeader
60
+
61
+ if (!channelId.contentEquals(other.channelId)) return false
62
+ if (tagId != other.tagId) return false
63
+ if (!frameId.contentEquals(other.frameId)) return false
64
+
65
+ return true
66
+ }
67
+
68
+ override fun hashCode(): Int {
69
+ var result = channelId.contentHashCode()
70
+ result = 31 * result + tagId
71
+ result = 31 * result + frameId.contentHashCode()
72
+ return result
73
+ }
74
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.framer.model
7
+
8
+ internal const val HEADER_SIZE = 5
9
+ internal const val CHANNEL_ID_INDEX = 0
10
+ internal const val CHANNEL_ID_SIZE = 2
11
+ internal const val TAG_ID_INDEX = 2
12
+ internal const val FRAME_ID_INDEX = 3
13
+ internal const val FRAME_ID_SIZE = 2
14
+ internal const val MAXIMUM_HEADER_SIZE = HEADER_SIZE + APDU_SIZE_SIZE
@@ -0,0 +1,21 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.utils
7
+
8
+ import com.ledger.devicesdk.shared.api.utils.isHexadecimal
9
+
10
+ internal fun Byte.isNotHexadecimal(): Boolean = !this.isHexadecimal()
11
+
12
+ @OptIn(ExperimentalStdlibApi::class)
13
+ internal fun Byte.isHexadecimal(): Boolean {
14
+ return try {
15
+ this.toHexString().isHexadecimal()
16
+ true
17
+ }
18
+ catch (e: NumberFormatException){
19
+ false
20
+ }
21
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2024 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.transport.utils
7
+
8
+ import com.ledger.devicesdk.shared.api.apdu.ApduParser
9
+ import com.ledger.devicesdk.shared.internal.transport.framer.model.ApduFramerHeader
10
+
11
+ internal fun ByteArray.extractFrameHeader(isUsbTransport: Boolean, parser: ApduParser) =
12
+ ApduFramerHeader(
13
+ channelId = if(isUsbTransport) parser.extract2BytesValue() else null,
14
+ tagId = parser.extractByteValue(),
15
+ frameId = parser.extract2BytesValue(),
16
+ )
17
+
18
+ internal fun ByteArray.extractApduSize(parser: ApduParser) = parser.extract2BytesValue()
@@ -0,0 +1,12 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2023 Ledger SAS
3
+ * SPDX-License-Identifier: LicenseRef-LEDGER
4
+ */
5
+
6
+ package com.ledger.devicesdk.shared.internal.utils
7
+
8
+ internal interface Controller {
9
+ fun start()
10
+
11
+ fun stop()
12
+ }