@pagopa/io-react-native-iso18013 0.7.0 → 0.8.1

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 (45) hide show
  1. package/IoReactNativeIso18013.podspec +1 -1
  2. package/README.md +1 -1
  3. package/android/build.gradle +3 -2
  4. package/android/gradle.properties +3 -3
  5. package/android/src/main/AndroidManifest.xml +17 -0
  6. package/android/src/main/AndroidManifestNew.xml +17 -0
  7. package/android/src/main/java/com/ioreactnativeiso18013/Enums.kt +42 -0
  8. package/android/src/main/java/com/ioreactnativeiso18013/IoNfcEngagementService.kt +17 -0
  9. package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeIso18013Module.kt +326 -132
  10. package/android/src/main/res/values/strings.xml +5 -0
  11. package/android/src/main/res/xml/nfc_engagement_apdu_service.xml +9 -0
  12. package/ios/IoReactNativeIso18013.mm +8 -9
  13. package/ios/IoReactNativeIso18013.swift +264 -191
  14. package/lib/module/cbor/cbor/README.md +3 -2
  15. package/lib/module/cbor/cose/README.md +7 -5
  16. package/lib/module/iso18013/iso18013-5/README.md +162 -42
  17. package/lib/module/iso18013/iso18013-5/error.js +1 -3
  18. package/lib/module/iso18013/iso18013-5/error.js.map +1 -1
  19. package/lib/module/iso18013/iso18013-5/index.js +1 -1
  20. package/lib/module/iso18013/iso18013-5/index.js.map +1 -1
  21. package/lib/module/iso18013/iso18013-5/proximity.js +29 -19
  22. package/lib/module/iso18013/iso18013-5/proximity.js.map +1 -1
  23. package/lib/module/iso18013/iso18013-7/README.md +2 -2
  24. package/lib/module/schema.js +1 -1
  25. package/lib/module/schema.js.map +1 -1
  26. package/lib/typescript/src/cbor/cbor/error.d.ts +5 -5
  27. package/lib/typescript/src/cbor/cose/error.d.ts +5 -5
  28. package/lib/typescript/src/iso18013/iso18013-5/error.d.ts +9 -9
  29. package/lib/typescript/src/iso18013/iso18013-5/error.d.ts.map +1 -1
  30. package/lib/typescript/src/iso18013/iso18013-5/index.d.ts +1 -1
  31. package/lib/typescript/src/iso18013/iso18013-5/index.d.ts.map +1 -1
  32. package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts +32 -13
  33. package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts.map +1 -1
  34. package/lib/typescript/src/iso18013/iso18013-7/error.d.ts +5 -5
  35. package/lib/typescript/src/schema.d.ts +7 -17
  36. package/lib/typescript/src/schema.d.ts.map +1 -1
  37. package/package.json +7 -7
  38. package/src/cbor/cbor/README.md +3 -2
  39. package/src/cbor/cose/README.md +7 -5
  40. package/src/iso18013/iso18013-5/README.md +162 -42
  41. package/src/iso18013/iso18013-5/error.ts +0 -2
  42. package/src/iso18013/iso18013-5/index.ts +3 -2
  43. package/src/iso18013/iso18013-5/proximity.ts +52 -23
  44. package/src/iso18013/iso18013-7/README.md +2 -2
  45. package/src/schema.ts +1 -1
@@ -3,24 +3,23 @@
3
3
 
4
4
  @interface RCT_EXTERN_MODULE(IoReactNativeIso18013, RCTEventEmitter)
5
5
 
6
- RCT_EXTERN_METHOD(start:(NSArray *)certificates
6
+ RCT_EXTERN_METHOD(startEngagement:(NSArray *)certificates
7
+ withEngagementModes:(NSArray *)engagementModes
8
+ withRetrievalMethods:(NSArray *)retrievalMethods
7
9
  withResolver:(RCTPromiseResolveBlock)resolve
8
10
  withRejecter:(RCTPromiseRejectBlock)reject)
9
11
 
10
- RCT_EXTERN_METHOD(getQrCodeString:(RCTPromiseResolveBlock)resolve
11
- withRejecter:(RCTPromiseRejectBlock)reject)
12
-
13
12
  RCT_EXTERN_METHOD(close:(RCTPromiseResolveBlock)resolve
14
13
  withRejecter:(RCTPromiseRejectBlock)reject)
15
14
 
16
15
  RCT_EXTERN_METHOD(generateResponse:(NSArray *)documents
17
- withAcceptedFields:(NSDictionary *)acceptedFields
18
- withResolver:(RCTPromiseResolveBlock)resolve
19
- withRejecter:(RCTPromiseRejectBlock)reject)
16
+ withAcceptedFields:(NSDictionary *)acceptedFields
17
+ withResolver:(RCTPromiseResolveBlock)resolve
18
+ withRejecter:(RCTPromiseRejectBlock)reject)
20
19
 
21
20
  RCT_EXTERN_METHOD(sendResponse:(NSString *)response
22
- withResolver:(RCTPromiseResolveBlock)resolve
23
- withRejecter:(RCTPromiseRejectBlock)reject)
21
+ withResolver:(RCTPromiseResolveBlock)resolve
22
+ withRejecter:(RCTPromiseRejectBlock)reject)
24
23
 
25
24
  RCT_EXTERN_METHOD(sendErrorResponse:(NSInteger *)code
26
25
  withResolver:(RCTPromiseResolveBlock)resolve
@@ -3,20 +3,18 @@ import IOWalletProximity
3
3
  import React
4
4
 
5
5
  @objc(IoReactNativeIso18013)
6
- class IoReactNativeIso18013: RCTEventEmitter {
6
+ class IoReactNativeIso18013: RCTEventEmitter, ISO18013Delegate {
7
7
 
8
8
  @objc
9
9
  override static func requiresMainQueueSetup() -> Bool {
10
10
  return true
11
11
  }
12
12
 
13
- override init() {
14
- super.init()
15
- setupProximityHandler()
16
- }
17
-
18
13
  /**
19
14
  Specifies supported events which will be emitted.
15
+ - `onQrCodeString`: Emitted when the QR Code payload is generated.
16
+ - `onNfcStarted`: Emitted when NFC starts successfully.
17
+ - `onNfcStopped`: Emitted when NFC stops successfully.
20
18
  - `onDeviceConnecting`: Emitted when the device is connecting to the verifier app.
21
19
  - `onDeviceConnected`: Emitted when the device is connected to the verifier app.
22
20
  - `onDocumentRequestReceived`: Emitted when a document request is received from the verifier app. Carries a payload containing the request data.
@@ -24,7 +22,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
24
22
  - `onError`: Emitted when an error occurs. Carries a payload containing the error data.
25
23
  */
26
24
  override func supportedEvents() -> [String]! {
27
- return ["onDeviceConnected", "onDeviceConnecting", "onDeviceDisconnected", "onDocumentRequestReceived", "onError", "unknown"]
25
+ return ["onQrCodeString", "onNfcStarted", "onNfcStopped", "onDeviceConnected", "onDeviceConnecting", "onDeviceDisconnected", "onDocumentRequestReceived", "onError", "unknown"]
28
26
  }
29
27
 
30
28
  /**
@@ -36,39 +34,146 @@ class IoReactNativeIso18013: RCTEventEmitter {
36
34
  typealias AcceptedFieldsDict = [String: [String: [String: Bool]]]
37
35
 
38
36
  /**
39
- Starts the proximity flow by allocating the necessary resources and initializing the Bluetooth stack.
37
+ ISO18013Delegate event handler
38
+ */
39
+ func onEvent(event: ISO18013Event) {
40
+ var eventName: String
41
+ var eventBody: [String: Any] = [:]
42
+
43
+ switch(event) {
44
+ case .qrCode(let qrCode):
45
+ eventName = "onQrCodeString"
46
+ eventBody = ["data": qrCode]
47
+ break
48
+
49
+ case .bleConnecting:
50
+ eventName = "onDeviceConnecting"
51
+ break
52
+
53
+ case .bleConnected:
54
+ eventName = "onDeviceConnected"
55
+ break
56
+
57
+ case .nfcEngagementStarted:
58
+ eventName = "onDeviceConnecting"
59
+ break
60
+
61
+ case .nfcEngagementDone:
62
+ eventName = "onDeviceConnected"
63
+ break
64
+
65
+ case .dataTransferStarted(let args):
66
+ eventName = "onDocumentRequestReceived"
67
+ /**
68
+ The outermost key represents the credential doctype, the inner key represents the namespace and the innermost key represents the requested fields with a boolean value. Example:
69
+ {
70
+ "org.iso.18013.5.1.mDL": {
71
+ "isAuthenticated": true,
72
+ "org.iso.18013.5.1": {
73
+ "hair_colour": true,
74
+ "given_name_national_character": true,
75
+ "family_name_national_character": true,
76
+ "given_name": true,
77
+ }
78
+ }
79
+ }
80
+ */
81
+ eventBody = [
82
+ // Here we either send the request or an empty string which signals that something went wrong.
83
+ "data": args.request.flatMap { deviceRequestToJson(request: $0) } ?? "",
84
+ "retrievalMethod": retrivalMethodToString(args.retrivalMethod)
85
+ ]
86
+ break
87
+
88
+ case .dataTransferStopped:
89
+ eventName = "onDeviceDisconnected"
90
+ break
91
+
92
+ case .nfcStarted:
93
+ eventName = "onNfcStarted"
94
+ break
95
+
96
+ case .nfcStopped:
97
+ eventName = "onNfcStopped"
98
+ break
99
+
100
+ case .error(let error):
101
+ eventName = "onError"
102
+ eventBody = ["error": error.localizedDescription]
103
+ break
104
+
105
+ default:
106
+ eventName = "unknown"
107
+ eventBody = ["error": "Received an unknown event"]
108
+ }
109
+
110
+ self.sendEvent(withName: eventName, body: eventBody)
111
+ }
112
+
113
+ /**
114
+ Starts proximity engagement with QR Code/NFC engagement and BLE/NFC retrieval.
40
115
  Resolves to true or rejects with an error code defined in ``ModuleErrorCodes``.
41
116
  - Parameters:
42
- - certificates: Two-dimensional array of base64 strings representing DER encoded X.509 certificate which are used to authenticate the verifier app
43
- - resolve: The promise to be resolved
44
- - reject: The promise to be rejected
45
- */
46
- @objc(start:withResolver:withRejecter:)
47
- func start(
117
+ - `certificates`: Two-dimensional array of base64 strings representing DER encoded X.509 certificate which are used to authenticate the verifier app
118
+ - `engagementModes`: Array of strings representing engamgnes modes (e.g., "QRCODE", "NFC")
119
+ - `retrievalMethods`: Array of strings representing data transfer modes (e.g., "BLE", "NFC")
120
+ - `resolve`: The promise to be resolved
121
+ - `reject`: The promise to be rejected
122
+ */
123
+ @objc(startEngagement:withEngagementModes:withRetrievalMethods:withResolver:withRejecter:)
124
+ func startEngagement(
48
125
  certificates: [Any],
126
+ engagementModes: [String],
127
+ retrievalMethods: [String],
49
128
  resolve: @escaping RCTPromiseResolveBlock,
50
129
  reject: @escaping RCTPromiseRejectBlock
51
130
  ){
52
131
  do {
53
132
  let certsData = try parseCertificates(certificates)
54
- try Proximity.shared.start(certsData)
133
+ let parsedEngagementModes = try parseEngagementMode(engagementModes)
134
+ let parsedRetrievalMethods = try parseDataTransferModes(retrievalMethods)
135
+
136
+ ISO18013.shared
137
+ .start(
138
+ certsData,
139
+ engagementModes: parsedEngagementModes,
140
+ retrivalMethods: parsedRetrievalMethods,
141
+ delegate: self,
142
+ isNfcLateEngagement: false
143
+ )
55
144
  resolve(true)
56
145
  } catch let proximityError as ProximityError {
57
- reject(ModuleErrorCodes.generateResponseError.rawValue, proximityError.description, proximityError)
146
+ reject(ModuleErrorCodes.startError.rawValue, proximityError.description, proximityError)
58
147
  } catch let parsingError as ParsingError{
59
- reject(ModuleErrorCodes.generateResponseError.rawValue, parsingError.description, parsingError)
148
+ reject(ModuleErrorCodes.startError.rawValue, parsingError.description, parsingError)
60
149
  } catch {
61
- reject(ModuleErrorCodes.generateResponseError.rawValue, error.localizedDescription, error)
150
+ reject(ModuleErrorCodes.startError.rawValue, error.localizedDescription, error)
62
151
  }
63
152
  }
64
153
 
154
+ /**
155
+ Closes connections, stops BLE, stops NFC and clears any resource.
156
+ Resolves to true after closing the connection or rejects with an error code defined in ``ModuleErrorCodes``.
157
+ - Parameters:
158
+ - resolve: The promise to be resolved.
159
+ - reject: The promise to be rejected.
160
+ */
161
+ @objc(close:withRejecter:)
162
+ func close(
163
+ resolve: @escaping RCTPromiseResolveBlock,
164
+ reject: @escaping RCTPromiseRejectBlock
165
+ ) {
166
+ ISO18013.shared.stop()
167
+ resolve(true)
168
+ }
169
+
65
170
  /**
66
171
  Utility function to parse an array coming from the React Native Bridge into an array of Data representing DER encoded X.509 certificates.
67
172
  - Parameters:
68
- - certificates: Two-dimensional array of base64 strings representing DER encoded X.509 certificate
69
- - Throws: `ParsingError` if the provided certificate is not a valid base64 encoded string.
70
- - Returns: A two-dimensional array of Data containing DER encoded X.509 certificates.
71
- */
173
+ - certificates: Two-dimensional array of base64 strings representing DER encoded X.509 certificate
174
+ - Throws: `ParsingError` if the provided certificate is not a valid base64 encoded string.
175
+ - Returns: A two-dimensional array of Data containing DER encoded X.509 certificates.
176
+ */
72
177
  private func parseCertificates(_ certificates: [Any]) throws -> [[Data]] {
73
178
  return try certificates.enumerated().compactMap { (chainIndex, item) in
74
179
  guard let certStrings = item as? [String] else {
@@ -84,22 +189,42 @@ class IoReactNativeIso18013: RCTEventEmitter {
84
189
  }
85
190
 
86
191
  /**
87
- Creates a QR code to be scanned in order to initialize the presentation.
88
- Resolves with a string containing the QR code or rejects with an error code defined in ``ModuleErrorCodes``.
192
+ Utility function to parse engagement mode strings into ISO18013EngagementMode enum values.
89
193
  - Parameters:
90
- - resolve: The promise to be resolved
91
- - reject: The promise to be rejected
194
+ - modes: Array of strings representing engamgnes modes (e.g., "QRCODE", "NFC")
195
+ - Throws: `ParsingError` if an invalid data transfer mode string is provided.
196
+ - Returns: An array of ISO18013EngagementMode values.
92
197
  */
93
- @objc(getQrCodeString:withRejecter:)
94
- func getQrCodeString(
95
- resolve: @escaping RCTPromiseResolveBlock,
96
- reject: @escaping RCTPromiseRejectBlock
97
- ) {
98
- do{
99
- let qrCodeString = try Proximity.shared.getQrCode()
100
- resolve(qrCodeString)
101
- } catch {
102
- reject(ModuleErrorCodes.getQrCodeError.rawValue, error.localizedDescription, error)
198
+ private func parseEngagementMode(_ modes: [String]) throws -> [ISO18013EngagementMode] {
199
+ return try modes.map { modeString in
200
+ switch modeString.lowercased() {
201
+ case "qrcode":
202
+ return .qrCode
203
+ case "nfc":
204
+ return .nfc
205
+ default:
206
+ throw ParsingError.dataTransferModeNotValid("Invalid data engagement mode: '\(modeString)'. Expected 'qrcode' or 'nfc'.")
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ Utility function to parse data transfer mode strings into ISO18013DataTransferMode enum values.
213
+ - Parameters:
214
+ - modes: Array of strings representing data transfer modes (e.g., "BLE", "NFC")
215
+ - Throws: `ParsingError` if an invalid data transfer mode string is provided.
216
+ - Returns: An array of ISO18013DataTransferMode values.
217
+ */
218
+ private func parseDataTransferModes(_ modes: [String]) throws -> [ISO18013DataTransferMode] {
219
+ return try modes.map { modeString in
220
+ switch modeString.lowercased() {
221
+ case "ble":
222
+ return .ble
223
+ case "nfc":
224
+ return .nfc
225
+ default:
226
+ throw ParsingError.dataTransferModeNotValid("Invalid data transfer mode: '\(modeString)'. Expected 'ble' or 'nfc'.")
227
+ }
103
228
  }
104
229
  }
105
230
 
@@ -108,15 +233,15 @@ class IoReactNativeIso18013: RCTEventEmitter {
108
233
  It checks if each element in the array has `issuerSignedContent`, `alias` and `docType` properties in order to build a ``IOWalletProximity.ProximityDocument``, then it appens it to the array.
109
234
  The result can be fed to ``IOWalletProximity.generateResponse``.
110
235
  - Parameters:
111
- - documents: An array containing documents. Each document is defined as a map containing:
112
- - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
113
- - alias which is the alias of the key used to sign the credential;
114
- - docType which is the document type.
236
+ - documents: An array containing documents. Each document is defined as a map containing:
237
+ - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
238
+ - alias which is the alias of the key used to sign the credential;
239
+ - docType which is the document type.
115
240
  - Throws: `ParsingError`:
116
- - If the provided documents array do not contain a dictionary;
117
- - If the provided dictionary doesn't adhere to the structure we expect;
118
- - If the issuerSignedContent is not a valid base64 or base64url encoded string;
119
- - If the creation of a ProximityDdocument fails.
241
+ - If the provided documents array do not contain a dictionary;
242
+ - If the provided dictionary doesn't adhere to the structure we expect;
243
+ - If the issuerSignedContent is not a valid base64 or base64url encoded string;
244
+ - If the creation of a ProximityDdocument fails.
120
245
  - Returns: An array of `ProximityDocument` containg the documents to be presented.
121
246
  */
122
247
  private func parseDocuments(documents: [Any]) throws -> [ProximityDocument] {
@@ -146,7 +271,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
146
271
  It checks if each element in the array is a dictionary where the key is a string, and the value is another dictionary. This nested dictionary has a string as its key and a boolean as its value, then it appens it to the array.
147
272
  The result can be fed to ``IOWalletProximity.generateResponse``.
148
273
  - Parameters:
149
- - acceptedFields: A dictionary of any elements. In order to be added to the result dictionary each element must be shaped as ``AcceptedFieldsDict`` thus as [String: [String: [String: Bool]]].
274
+ - acceptedFields: A dictionary of any elements. In order to be added to the result dictionary each element must be shaped as ``AcceptedFieldsDict`` thus as [String: [String: [String: Bool]]].
150
275
  - Throws: `ParsingError` if a value doesn't has the ``AcceptedFieldsDict`` shape or the result dictionary is empty.
151
276
  - Returns: An ``AcceptedFieldsDict`` containg the accepted fields to be presented.
152
277
  */
@@ -171,26 +296,26 @@ class IoReactNativeIso18013: RCTEventEmitter {
171
296
  It parses the untyped ``documents`` and ``acceptedFields`` parameters and feeds them to the ``IOWalletProximity.generateDeviceResponse`` function.
172
297
  Resolves with a base64 encoded response or rejects with an error code defined in ``ModuleErrorCodes``.
173
298
  - Parameters:
174
- - documents: An array containing documents. Each document is defined as a map containing:
175
- - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
176
- - alias which is the alias of the key used to sign the credential;
177
- - docType which is the document type.
178
- - acceptedFields: A dictionary of elements, where each element must adhere to the structure of AcceptedFieldsDict—specifically, a [String: [String: [String: Bool]]]. The outermost key represents the credential doctype. The inner dictionary contains namespaces, and for each namespace, there is another dictionary mapping requested claims to a boolean value, which indicates whether the user is willing to present the corresponding claim. Example:
179
-
180
-
181
- {
182
- "org.iso.18013.5.1.mDL": {
183
- "org.iso.18013.5.1": {
184
- "hair_colour": true,
185
- "given_name_national_character": true,
186
- "family_name_national_character": true,
187
- "given_name": true,
188
- }
189
- }
190
- }
191
-
192
- - resolve: The promise to be resolved.
193
- - reject: The promise to be rejected.
299
+ - documents: An array containing documents. Each document is defined as a map containing:
300
+ - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
301
+ - alias which is the alias of the key used to sign the credential;
302
+ - docType which is the document type.
303
+ - acceptedFields: A dictionary of elements, where each element must adhere to the structure of AcceptedFieldsDict—specifically, a [String: [String: [String: Bool]]]. The outermost key represents the credential doctype. The inner dictionary contains namespaces, and for each namespace, there is another dictionary mapping requested claims to a boolean value, which indicates whether the user is willing to present the corresponding claim. Example:
304
+
305
+
306
+ {
307
+ "org.iso.18013.5.1.mDL": {
308
+ "org.iso.18013.5.1": {
309
+ "hair_colour": true,
310
+ "given_name_national_character": true,
311
+ "family_name_national_character": true,
312
+ "given_name": true,
313
+ }
314
+ }
315
+ }
316
+
317
+ - resolve: The promise to be resolved.
318
+ - reject: The promise to be rejected.
194
319
  */
195
320
  @objc(generateResponse:withAcceptedFields:withResolver:withRejecter:)
196
321
  func generateResponse(
@@ -202,7 +327,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
202
327
  do {
203
328
  let parsedDocuments = try parseDocuments(documents: documents)
204
329
  let items = try parseAcceptedFields(acceptedFields: acceptedFields)
205
- let deviceResponse = try Proximity.shared.generateDeviceResponse(items: items, documents: parsedDocuments, sessionTranscript: nil)
330
+ let deviceResponse = try ISO18013.shared.generateDeviceResponse(items: items, documents: parsedDocuments, sessionTranscript: nil)
206
331
  let strDeviceResponse = Data(deviceResponse).base64EncodedString()
207
332
  resolve(strDeviceResponse)
208
333
  }
@@ -221,9 +346,9 @@ class IoReactNativeIso18013: RCTEventEmitter {
221
346
  Currently there's not evidence of the verifier app responding to this request, thus we don't handle the response.
222
347
  Resolves with a true boolean in case of success or rejects with an error code defined in ``ModuleErrorCodes``.
223
348
  - Parameters:
224
- - response: A base64 encoded string containing the response generated by ``generateResponse``
225
- - resolve: The promise to be resolved
226
- - reject: The promise to be rejected
349
+ - response: A base64 encoded string containing the response generated by ``generateResponse``
350
+ - resolve: The promise to be resolved
351
+ - reject: The promise to be rejected
227
352
  */
228
353
  @objc(sendResponse:withResolver:withRejecter:)
229
354
  func sendResponse(
@@ -234,7 +359,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
234
359
  do{
235
360
  if let responseData = Data(base64Encoded: response) {
236
361
  let decodedResponse = [UInt8](responseData)
237
- try Proximity.shared.dataPresentation(decodedResponse)
362
+ try ISO18013.shared.dataPresentation(decodedResponse)
238
363
  resolve(true)
239
364
  }
240
365
  }catch let error {
@@ -246,21 +371,21 @@ class IoReactNativeIso18013: RCTEventEmitter {
246
371
  Sends an error response during the presentation according to the SessionData status codes defined in table 20 of the ISO18013-5 standard.
247
372
  Resolves to true or rejects with an error code defined in ``ModuleErrorCodes``.
248
373
  - Parameters:
249
- - code: The status error to be sent is an integer of type ``SessionDataStatus``:
250
- ```
251
- 10 -> Error: session encryption
252
- 11 -> Error: CBOR decoding
253
- 20 -> Session termination
254
- ```
255
- - resolve: The promise to be resolved.
256
- - reject: The promise to be rejected.
374
+ - code: The status error to be sent is an integer of type ``SessionDataStatus``:
375
+ ```
376
+ 10 -> Error: session encryption
377
+ 11 -> Error: CBOR decoding
378
+ 20 -> Session termination
379
+ ```
380
+ - resolve: The promise to be resolved.
381
+ - reject: The promise to be rejected.
257
382
  */
258
383
  @objc(sendErrorResponse:withResolver:withRejecter:)
259
384
  func sendErrorResponse(code: UInt64, _ resolve: @escaping RCTPromiseResolveBlock,
260
- reject: @escaping RCTPromiseRejectBlock){
385
+ reject: @escaping RCTPromiseRejectBlock){
261
386
  do{
262
387
  if let statusEnum = SessionDataStatus(rawValue: code) {
263
- try Proximity.shared.errorPresentation(statusEnum)
388
+ try ISO18013.shared.errorPresentation(statusEnum)
264
389
  } else {
265
390
  reject(ModuleErrorCodes.sendErrorResponseError.rawValue, "Invalid status code provided: \(code)", nil)
266
391
  }
@@ -270,27 +395,10 @@ class IoReactNativeIso18013: RCTEventEmitter {
270
395
  }
271
396
  }
272
397
 
273
-
274
- /**
275
- Closes the bluetooth connection and clears any resource.
276
- Resolves to true after closing the connection or rejects with an error code defined in ``ModuleErrorCodes``.
277
- - Parameters:
278
- - resolve: The promise to be resolved.
279
- - reject: The promise to be rejected.
280
- */
281
- @objc(close:withRejecter:)
282
- func close(
283
- resolve: @escaping RCTPromiseResolveBlock,
284
- reject: @escaping RCTPromiseRejectBlock
285
- ) {
286
- Proximity.shared.stop()
287
- resolve(true)
288
- }
289
-
290
398
  /**
291
399
  Converts a device requested from the `onDocumentRequestReceived` callback into a serializable JSON.
292
400
  - Parameters:
293
- - request: The request returned from `onDocumentRequestReceived` which contains an array of tuples consists of a doctype, namespaces and the requested claims with a boolean value indicating wether or not the device which is making the request has an intent to retain the data.
401
+ - request: The request returned from `onDocumentRequestReceived` which contains an array of tuples consists of a doctype, namespaces and the requested claims with a boolean value indicating wether or not the device which is making the request has an intent to retain the data.
294
402
  - Returns: A JSON string representing the device request or nil if an error occurs.
295
403
  */
296
404
  private func deviceRequestToJson(request: [(docType: String, nameSpaces: [String: [String: Bool]], isAuthenticated: Bool)]?) -> String? {
@@ -320,86 +428,36 @@ class IoReactNativeIso18013: RCTEventEmitter {
320
428
  }
321
429
  }
322
430
 
323
- /**
324
- Sets the proximity handler along with the possible dispatched events and their callbacks.
325
- The events are then sent to React Native via `RCTEventEmitter`.
326
- */
327
- private func setupProximityHandler() {
328
- Proximity.shared.proximityHandler = { [weak self] event in
329
- guard let self = self else { return }
330
- var eventName: String
331
- var eventBody: [String: Any] = [:]
332
-
333
- switch event {
334
- case .onDeviceConnecting:
335
- eventName = "onDeviceConnecting"
336
- case .onDeviceConnected:
337
- eventName = "onDeviceConnected"
338
- case .onDocumentRequestReceived(let request):
339
- eventName = "onDocumentRequestReceived"
340
- if let request = request {
341
- /**
342
- The outermost key represents the credential doctype, the inner key represents the namespace and the innermost key represents the requested fields with a boolean value. Example:
343
- {
344
- "org.iso.18013.5.1.mDL": {
345
- "isAuthenticated": true,
346
- "org.iso.18013.5.1": {
347
- "hair_colour": true,
348
- "given_name_national_character": true,
349
- "family_name_national_character": true,
350
- "given_name": true,
351
- }
352
- }
353
- }
354
- */
355
- let jsonString = deviceRequestToJson(request: request)
356
- // Here we either send the request or an empty string which signals that something went wrong.
357
- eventBody = ["data": jsonString ?? ""]
358
- }
359
- case .onDeviceDisconnected:
360
- eventName = "onDeviceDisconnected"
361
- case .onError(let error):
362
- eventName = "onError"
363
- eventBody = ["error": error.localizedDescription]
364
- default:
365
- eventName = "unknown"
366
- eventBody = ["error": "Received an unknown event"]
367
- }
368
-
369
- self.sendEvent(withName: eventName, body: eventBody)
370
- }
371
- }
372
-
373
431
  /**
374
432
  Generates a CBOR encoded device response for ISO 18013-7 mDL remote presentation using OID4VP.
375
433
  Resolves with the base64 encoded device response or rejects with an error code defined in ``ModuleErrorCodes``.
376
434
  - Parameters:
377
- - clientId: The client id extracted from OID4VP session.
378
- - responseUri: The response URI extracted from OID4VP session.
379
- - authorizationRequestNonce - The authorization request nonce extracted from OID4VP session.
380
- - mdocGeneratedNonce - the mdoc generated nonce to be generated.
381
- - documents: An array containing documents. Each document is defined as a map containing:
382
- - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
383
- - alias which is the alias of the key used to sign the credential;
384
- - docType which is the document type.
385
- - acceptedFields: A dictionary of elements, where each element must adhere to the structure of AcceptedFieldsDict—specifically, a `[String: [String: [String: Bool]]]`. The outermost key represents the credential doctype. The inner dictionary contains namespaces, and for each namespace, there is another dictionary mapping requested claims to a boolean value, which indicates whether the user is willing to present the corresponding claim. Example:
386
-
387
-
388
- {
389
- "org.iso.18013.5.1.mDL": {
390
- "org.iso.18013.5.1": {
391
- "hair_colour": true,
392
- "given_name_national_character": true,
393
- "family_name_national_character": true,
394
- "given_name": true,
395
- },
396
- {...}
397
- },
398
- {...}
399
- }
400
-
401
- - resolve: The promise to be resolved.
402
- - reject: The promise to be rejected.
435
+ - clientId: The client id extracted from OID4VP session.
436
+ - responseUri: The response URI extracted from OID4VP session.
437
+ - authorizationRequestNonce - The authorization request nonce extracted from OID4VP session.
438
+ - mdocGeneratedNonce - the mdoc generated nonce to be generated.
439
+ - documents: An array containing documents. Each document is defined as a map containing:
440
+ - issuerSignedContent which is a base64 or base64url encoded string representing the credential;
441
+ - alias which is the alias of the key used to sign the credential;
442
+ - docType which is the document type.
443
+ - acceptedFields: A dictionary of elements, where each element must adhere to the structure of AcceptedFieldsDict—specifically, a `[String: [String: [String: Bool]]]`. The outermost key represents the credential doctype. The inner dictionary contains namespaces, and for each namespace, there is another dictionary mapping requested claims to a boolean value, which indicates whether the user is willing to present the corresponding claim. Example:
444
+
445
+
446
+ {
447
+ "org.iso.18013.5.1.mDL": {
448
+ "org.iso.18013.5.1": {
449
+ "hair_colour": true,
450
+ "given_name_national_character": true,
451
+ "family_name_national_character": true,
452
+ "given_name": true,
453
+ },
454
+ {...}
455
+ },
456
+ {...}
457
+ }
458
+
459
+ - resolve: The promise to be resolved.
460
+ - reject: The promise to be rejected.
403
461
  */
404
462
  @objc(generateOID4VPDeviceResponse:withResponseUri:withAuthorizationRequestNonce:withMdocGeneratedNonce:withDocuments:withAcceptedFields:withResolver:withRejecter:)
405
463
  func generateOID4VPDeviceResponse(
@@ -414,7 +472,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
414
472
  ) {
415
473
 
416
474
  do {
417
- let sessionTranscript = Proximity.shared.generateOID4VPSessionTranscriptCBOR(
475
+ let sessionTranscript = ISO18013.shared.generateOID4VPSessionTranscriptCBOR(
418
476
  clientId: clientId,
419
477
  responseUri: responseUri,
420
478
  authorizationRequestNonce: authorizationRequestNonce,
@@ -423,7 +481,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
423
481
 
424
482
  let documentsAsProximityDocument = try parseDocuments(documents: documents)
425
483
  let items = try parseAcceptedFields(acceptedFields: acceptedFields)
426
- let response = try Proximity.shared.generateDeviceResponse(items: items, documents: documentsAsProximityDocument, sessionTranscript: sessionTranscript)
484
+ let response = try ISO18013.shared.generateDeviceResponse(items: items, documents: documentsAsProximityDocument, sessionTranscript: sessionTranscript)
427
485
  resolve(Data(response).base64EncodedString())
428
486
  } catch let parsingError as ParsingError{
429
487
  reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, parsingError.description, parsingError)
@@ -434,34 +492,49 @@ class IoReactNativeIso18013: RCTEventEmitter {
434
492
  reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, error.localizedDescription, error)
435
493
  }
436
494
  }
437
-
495
+
438
496
  /**
439
497
  Custom Error which is thrown when a parsing error occurs in our utility functions which converts data from the bridge to what
440
498
  our underlying functions expect.
441
499
  This is needed in order to provide a customized description which can include more debug information.
442
500
  */
443
501
  enum ParsingError : Error, CustomStringConvertible {
444
- case documentsNotValid(String)
445
- case certificatesNotValid(String)
446
- case acceptedFieldsNotValid(String)
447
-
448
- public var description: String {
449
- switch(self) {
450
- case .documentsNotValid(let message):
451
- return message
452
- case .certificatesNotValid(let message):
453
- return message
454
- case .acceptedFieldsNotValid(let message):
455
- return message
456
- }
502
+ case documentsNotValid(String)
503
+ case certificatesNotValid(String)
504
+ case acceptedFieldsNotValid(String)
505
+ case dataTransferModeNotValid(String)
506
+
507
+ public var description: String {
508
+ switch(self) {
509
+ case .documentsNotValid(let message):
510
+ return message
511
+ case .certificatesNotValid(let message):
512
+ return message
513
+ case .acceptedFieldsNotValid(let message):
514
+ return message
515
+ case .dataTransferModeNotValid(let message):
516
+ return message
457
517
  }
518
+ }
458
519
  }
459
520
 
521
+ /**
522
+ Maps a retrival method enum value to its string representation.
523
+ */
524
+ private func retrivalMethodToString(_ method: ISO18013DataTransferMode) -> String {
525
+ switch method {
526
+ case .ble:
527
+ return "ble"
528
+ case .nfc:
529
+ return "nfc"
530
+ }
531
+ }
532
+
460
533
  // Errors which this module uses to reject a promise
461
534
  private enum ModuleErrorCodes: String, CaseIterable {
462
535
  // ISO18013-5 related errors
463
536
  case startError = "START_ERROR"
464
- case getQrCodeError = "GET_QR_CODE_ERROR"
537
+ case stopError = "STOP_ERROR"
465
538
  case sendResponseError = "SEND_RESPONSE_ERROR"
466
539
  case sendErrorResponseError = "SEND_ERROR_RESPONSE_ERROR"
467
540
  case generateResponseError = "GENERATE_RESPONSE_ERROR"