@pagopa/io-react-native-iso18013 0.3.0 → 0.4.0

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 (91) hide show
  1. package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeCborModule.kt +67 -164
  2. package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeIso18013Module.kt +95 -140
  3. package/ios/Base64Utils.swift +10 -3
  4. package/ios/IoReactNativeCbor.swift +55 -125
  5. package/ios/IoReactNativeIso18013.swift +87 -116
  6. package/lib/module/cbor/cbor/README.md +37 -5
  7. package/lib/module/cbor/cbor/error.js +15 -0
  8. package/lib/module/cbor/cbor/error.js.map +1 -0
  9. package/lib/module/cbor/cbor/index.js +1 -0
  10. package/lib/module/cbor/cbor/index.js.map +1 -1
  11. package/lib/module/cbor/cose/README.md +39 -9
  12. package/lib/module/cbor/cose/error.js +17 -0
  13. package/lib/module/cbor/cose/error.js.map +1 -0
  14. package/lib/module/cbor/cose/index.js +1 -0
  15. package/lib/module/cbor/cose/index.js.map +1 -1
  16. package/lib/module/iso18013/index.js +0 -1
  17. package/lib/module/iso18013/index.js.map +1 -1
  18. package/lib/module/iso18013/iso18013-5/README.md +52 -16
  19. package/lib/module/iso18013/iso18013-5/error.js +25 -0
  20. package/lib/module/iso18013/iso18013-5/error.js.map +1 -0
  21. package/lib/module/iso18013/iso18013-5/index.js +3 -2
  22. package/lib/module/iso18013/iso18013-5/index.js.map +1 -1
  23. package/lib/module/iso18013/iso18013-5/proximity.js +1 -9
  24. package/lib/module/iso18013/iso18013-5/proximity.js.map +1 -1
  25. package/lib/module/iso18013/iso18013-5/{schema.js → request.js} +1 -20
  26. package/lib/module/iso18013/iso18013-5/request.js.map +1 -0
  27. package/lib/module/iso18013/iso18013-7/README.md +38 -9
  28. package/lib/module/iso18013/iso18013-7/error.js +15 -0
  29. package/lib/module/iso18013/iso18013-7/error.js.map +1 -0
  30. package/lib/module/iso18013/iso18013-7/index.js +1 -0
  31. package/lib/module/iso18013/iso18013-7/index.js.map +1 -1
  32. package/lib/module/iso18013/iso18013-7/remote.js +0 -12
  33. package/lib/module/iso18013/iso18013-7/remote.js.map +1 -1
  34. package/lib/module/schema.js +46 -0
  35. package/lib/module/schema.js.map +1 -0
  36. package/lib/typescript/src/cbor/cbor/error.d.ts +68 -0
  37. package/lib/typescript/src/cbor/cbor/error.d.ts.map +1 -0
  38. package/lib/typescript/src/cbor/cbor/index.d.ts +1 -1
  39. package/lib/typescript/src/cbor/cbor/index.d.ts.map +1 -1
  40. package/lib/typescript/src/cbor/cose/error.d.ts +68 -0
  41. package/lib/typescript/src/cbor/cose/error.d.ts.map +1 -0
  42. package/lib/typescript/src/cbor/cose/index.d.ts +1 -1
  43. package/lib/typescript/src/cbor/cose/index.d.ts.map +1 -1
  44. package/lib/typescript/src/iso18013/index.d.ts +0 -1
  45. package/lib/typescript/src/iso18013/index.d.ts.map +1 -1
  46. package/lib/typescript/src/iso18013/iso18013-5/error.d.ts +73 -0
  47. package/lib/typescript/src/iso18013/iso18013-5/error.d.ts.map +1 -0
  48. package/lib/typescript/src/iso18013/iso18013-5/index.d.ts +3 -2
  49. package/lib/typescript/src/iso18013/iso18013-5/index.d.ts.map +1 -1
  50. package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts +2 -7
  51. package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts.map +1 -1
  52. package/lib/typescript/src/iso18013/iso18013-5/{schema.d.ts → request.d.ts} +1 -16
  53. package/lib/typescript/src/iso18013/iso18013-5/request.d.ts.map +1 -0
  54. package/lib/typescript/src/iso18013/iso18013-7/error.d.ts +68 -0
  55. package/lib/typescript/src/iso18013/iso18013-7/error.d.ts.map +1 -0
  56. package/lib/typescript/src/iso18013/iso18013-7/index.d.ts +2 -1
  57. package/lib/typescript/src/iso18013/iso18013-7/index.d.ts.map +1 -1
  58. package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts +0 -14
  59. package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts.map +1 -1
  60. package/lib/typescript/src/schema.d.ts +76 -0
  61. package/lib/typescript/src/schema.d.ts.map +1 -0
  62. package/package.json +1 -1
  63. package/src/cbor/cbor/README.md +37 -5
  64. package/src/cbor/cbor/error.ts +23 -0
  65. package/src/cbor/cbor/index.ts +5 -1
  66. package/src/cbor/cose/README.md +39 -9
  67. package/src/cbor/cose/error.ts +23 -0
  68. package/src/cbor/cose/index.ts +5 -1
  69. package/src/iso18013/index.ts +0 -7
  70. package/src/iso18013/iso18013-5/README.md +52 -16
  71. package/src/iso18013/iso18013-5/error.ts +35 -0
  72. package/src/iso18013/iso18013-5/index.ts +9 -4
  73. package/src/iso18013/iso18013-5/proximity.ts +2 -10
  74. package/src/iso18013/iso18013-5/{schema.ts → request.ts} +0 -20
  75. package/src/iso18013/iso18013-7/README.md +38 -9
  76. package/src/iso18013/iso18013-7/error.ts +21 -0
  77. package/src/iso18013/iso18013-7/index.ts +5 -4
  78. package/src/iso18013/iso18013-7/remote.ts +0 -20
  79. package/src/schema.ts +55 -0
  80. package/lib/module/cbor/cbor/failure.js +0 -2
  81. package/lib/module/cbor/cbor/failure.js.map +0 -1
  82. package/lib/module/cbor/cose/failure.js +0 -2
  83. package/lib/module/cbor/cose/failure.js.map +0 -1
  84. package/lib/module/iso18013/iso18013-5/schema.js.map +0 -1
  85. package/lib/typescript/src/cbor/cbor/failure.d.ts +0 -15
  86. package/lib/typescript/src/cbor/cbor/failure.d.ts.map +0 -1
  87. package/lib/typescript/src/cbor/cose/failure.d.ts +0 -15
  88. package/lib/typescript/src/cbor/cose/failure.d.ts.map +0 -1
  89. package/lib/typescript/src/iso18013/iso18013-5/schema.d.ts.map +0 -1
  90. package/src/cbor/cbor/failure.ts +0 -18
  91. package/src/cbor/cose/failure.ts +0 -20
@@ -5,10 +5,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
5
5
  import com.facebook.react.bridge.ReactMethod
6
6
  import com.facebook.react.bridge.Promise
7
7
  import com.facebook.react.bridge.ReadableMap
8
- import com.facebook.react.bridge.WritableMap
9
- import com.facebook.react.bridge.WritableNativeMap
10
8
  import it.pagopa.io.wallet.cbor.cose.COSEManager
11
- import it.pagopa.io.wallet.cbor.cose.FailureReason
12
9
  import it.pagopa.io.wallet.cbor.cose.SignWithCOSEResult
13
10
  import it.pagopa.io.wallet.cbor.parser.CBorParser
14
11
 
@@ -21,189 +18,95 @@ class IoReactNativeCborModule(reactContext: ReactApplicationContext) :
21
18
 
22
19
  @ReactMethod
23
20
  fun decode(data: String, promise: Promise) {
24
- val buffer = try {
25
- Base64Utils.decodeBase64AndBase64Url(data)
26
- } catch (e: Exception) {
27
- ModuleException.INVALID_ENCODING.reject(
28
- promise,
29
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
30
- )
31
- return;
32
- }
33
-
34
21
  try {
35
- CBorParser(buffer).toJson()?.let {
36
- promise.resolve(it)
37
- } ?: run {
38
- ModuleException.UNABLE_TO_DECODE.reject(promise)
39
- }
22
+ val buffer = Base64Utils.decodeBase64AndBase64Url(data)
23
+ val result = CBorParser(buffer).toJson()
24
+ promise.resolve(result)
40
25
  } catch (e: Exception) {
41
- ModuleException.UNKNOWN_EXCEPTION.reject(
42
- promise,
43
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
44
- )
26
+ promise.reject(ModuleErrorCodes.DECODE_ERROR, e.message, e)
45
27
  }
46
28
  }
47
29
 
48
- @ReactMethod
49
- fun decodeDocuments(data: String, promise: Promise) {
50
- val buffer = try {
51
- Base64Utils.decodeBase64AndBase64Url(data)
52
- } catch (e: Exception) {
53
- ModuleException.INVALID_ENCODING.reject(
54
- promise,
55
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
56
- )
57
- return;
58
- }
59
- try {
60
- CBorParser(buffer).documentsCborToJson(separateElementIdentifier = true, onComplete = {
61
- promise.resolve(it)
62
- }) { ex ->
63
- ModuleException.UNABLE_TO_DECODE.reject(
64
- promise,
65
- Pair(ERROR_USER_INFO_KEY, ex.message.orEmpty())
66
- )
67
- }
68
- } catch (e: Exception) {
69
- ModuleException.UNKNOWN_EXCEPTION.reject(
70
- promise,
71
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
72
- )
73
- }
74
- }
75
30
 
76
- @ReactMethod
77
- fun decodeIssuerSigned(issuerSigned: String, promise: Promise) {
78
- val buffer = try {
79
- Base64Utils.decodeBase64AndBase64Url(issuerSigned)
80
- } catch (e: Exception) {
81
- ModuleException.INVALID_ENCODING.reject(
82
- promise,
83
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
84
- )
85
- return;
86
- }
87
- try {
88
- CBorParser(buffer).issuerSignedCborToJson(separateElementIdentifier = true).let {
89
- if (it == null) {
90
- ModuleException.UNABLE_TO_DECODE.reject(
91
- promise,
92
- Pair(ERROR_USER_INFO_KEY, "Unable to decode passed CBOR")
93
- )
94
- return
95
- }
96
- promise.resolve(it)
31
+ @ReactMethod
32
+ fun decodeDocuments(data: String, promise: Promise) {
33
+ try {
34
+ val buffer = Base64Utils.decodeBase64AndBase64Url(data)
35
+ CBorParser(buffer).documentsCborToJson(separateElementIdentifier = true, onComplete = {
36
+ promise.resolve(it)
37
+ }, onError = { e ->
38
+ promise.reject(ModuleErrorCodes.DECODE_DOCUMENTS_ERROR, e.message, e)
39
+ })
40
+ } catch (e: Exception) {
41
+ promise.reject(ModuleErrorCodes.DECODE_DOCUMENTS_ERROR, e.message, e)
97
42
  }
98
- } catch (e: Exception) {
99
- ModuleException.UNKNOWN_EXCEPTION.reject(
100
- promise,
101
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
102
- )
103
43
  }
104
- }
105
44
 
106
- @ReactMethod
107
- fun sign(payload: String, keyTag: String, promise: Promise) {
108
- val data = try {
109
- Base64Utils.decodeBase64AndBase64Url(payload)
110
- } catch (e: Exception) {
111
- ModuleException.INVALID_ENCODING.reject(
112
- promise,
113
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
114
- )
115
- return;
45
+ @ReactMethod
46
+ fun decodeIssuerSigned(issuerSigned: String, promise: Promise) {
47
+ try {
48
+ val buffer =
49
+ Base64Utils.decodeBase64AndBase64Url(issuerSigned)
50
+ val result =
51
+ CBorParser(buffer).issuerSignedCborToJson(separateElementIdentifier = true) ?: run {
52
+ // We don't have the exact error here for some reason
53
+ promise.reject(
54
+ ModuleErrorCodes.DECODE_ISSUER_SIGNED_ERROR,
55
+ "An error occurred while decoding the issuer signed content"
56
+ )
57
+ }
58
+ promise.resolve(result)
59
+ } catch (e: Exception) {
60
+ promise.reject(ModuleErrorCodes.DECODE_ISSUER_SIGNED_ERROR, e.message, e)
61
+ }
116
62
  }
117
63
 
64
+ @ReactMethod
65
+ fun sign(payload: String, keyTag: String, promise: Promise){
118
66
  try {
119
- val result = COSEManager().signWithCOSE(
120
- data = data,
121
- alias = keyTag
122
- )
123
- when (result) {
124
- is SignWithCOSEResult.Failure -> {
125
- when (result.reason) {
126
- FailureReason.NO_KEY -> ModuleException.PUBLIC_KEY_NOT_FOUND.reject(
127
- promise,
128
- Pair(ERROR_USER_INFO_KEY, result.reason.msg)
129
- )
130
-
131
- FailureReason.FAIL_TO_SIGN -> ModuleException.UNABLE_TO_SIGN.reject(
132
- promise,
133
- Pair(ERROR_USER_INFO_KEY, result.reason.msg)
134
- )
135
-
136
- else -> ModuleException.UNKNOWN_EXCEPTION.reject(
137
- promise,
138
- Pair(ERROR_USER_INFO_KEY, result.reason.msg)
139
- )
67
+ val data = Base64Utils.decodeBase64AndBase64Url(payload)
68
+ val result = COSEManager().signWithCOSE(
69
+ data = data,
70
+ alias = keyTag
71
+ )
72
+ when (result) {
73
+ is SignWithCOSEResult.Failure -> {
74
+ // We don't have a throwable to pass here from the onError callback
75
+ promise.reject(ModuleErrorCodes.SIGN_ERROR, result.reason.msg)
76
+ }
77
+ is SignWithCOSEResult.Success -> {
78
+ promise.resolve(Base64Utils.encodeBase64(result.signature))
140
79
  }
141
80
  }
142
-
143
- is SignWithCOSEResult.Success -> {
144
- promise.resolve(Base64Utils.encodeBase64(result.signature))
145
- }
81
+ } catch (e: Exception) {
82
+ promise.reject(ModuleErrorCodes.SIGN_ERROR, e.message, e)
146
83
  }
147
- } catch (e: Exception) {
148
- ModuleException.UNKNOWN_EXCEPTION.reject(
149
- promise,
150
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
151
- )
152
- }
153
- }
154
-
155
- @ReactMethod
156
- fun verify(sign1Data: String, publicKey: ReadableMap, promise: Promise) {
157
- val data = try {
158
- Base64Utils.decodeBase64AndBase64Url(sign1Data)
159
- } catch (e: Exception) {
160
- ModuleException.INVALID_ENCODING.reject(
161
- promise,
162
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
163
- )
164
- return;
165
84
  }
166
85
 
167
- try {
168
- val result = COSEManager().verifySign1FromJWK(
169
- dataSigned = data,
170
- jwk = publicKey.toString()
171
- )
172
- promise.resolve(result)
173
- } catch (e: Exception) {
174
- ModuleException.UNKNOWN_EXCEPTION.reject(
175
- promise,
176
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
177
- )
86
+ @ReactMethod
87
+ fun verify(sign1Data: String, publicKey: ReadableMap, promise: Promise) {
88
+ try {
89
+ val data = Base64Utils.decodeBase64AndBase64Url(sign1Data)
90
+ val result = COSEManager().verifySign1FromJWK(
91
+ dataSigned = data,
92
+ jwk = publicKey.toString()
93
+ )
94
+ promise.resolve(result)
95
+ } catch (e: Exception) {
96
+ promise.reject(ModuleErrorCodes.VERIFY_ERROR, e.message, e)
97
+ }
178
98
  }
179
- }
180
99
 
181
100
  companion object {
182
101
  const val NAME = "IoReactNativeCbor"
183
- const val ERROR_USER_INFO_KEY = "error"
184
-
185
- private enum class ModuleException(
186
- val ex: Exception
187
- ) {
188
- UNABLE_TO_DECODE(Exception("UNABLE_TO_DECODE")),
189
- PUBLIC_KEY_NOT_FOUND(Exception("PUBLIC_KEY_NOT_FOUND")),
190
- UNABLE_TO_SIGN(Exception("UNABLE_TO_SIGN")),
191
- INVALID_ENCODING(Exception("INVALID_ENCODING")),
192
- UNKNOWN_EXCEPTION(Exception("UNKNOWN_EXCEPTION"));
193
-
194
- fun reject(
195
- promise: Promise, vararg args: Pair<String, String>
196
- ) {
197
- exMap(*args).let {
198
- promise.reject(it.first, ex.message, it.second)
199
- }
200
- }
201
102
 
202
- private fun exMap(vararg args: Pair<String, String>): Pair<String, WritableMap> {
203
- val writableMap = WritableNativeMap()
204
- args.forEach { writableMap.putString(it.first, it.second) }
205
- return Pair(this.ex.message ?: "UNKNOWN", writableMap)
206
- }
103
+ // Errors which this module uses to reject a promise
104
+ private object ModuleErrorCodes {
105
+ const val DECODE_ERROR = "DECODE_ERROR"
106
+ const val DECODE_DOCUMENTS_ERROR = "DECODE_DOCUMENTS_ERROR"
107
+ const val DECODE_ISSUER_SIGNED_ERROR = "DECODE_ISSUER_SIGNED_ERROR"
108
+ const val SIGN_ERROR = "SIGN_ERROR"
109
+ const val VERIFY_ERROR = "VERIFY_ERROR"
207
110
  }
208
111
  }
209
112
  }
@@ -9,8 +9,6 @@ import com.facebook.react.bridge.ReadableArray
9
9
  import com.facebook.react.bridge.ReadableMap
10
10
  import com.facebook.react.bridge.ReadableType
11
11
  import com.facebook.react.bridge.WritableMap
12
- import com.facebook.react.bridge.WritableNativeMap
13
- import com.ioreactnativeiso18013.IoReactNativeCborModule.Companion.ERROR_USER_INFO_KEY
14
12
  import it.pagopa.io.wallet.proximity.OpenID4VP
15
13
  import it.pagopa.io.wallet.proximity.bluetooth.BleRetrievalMethod
16
14
  import it.pagopa.io.wallet.proximity.qr_code.QrEngagement
@@ -64,11 +62,7 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
64
62
  setupProximityHandler()
65
63
  promise.resolve(true)
66
64
  } catch (e: Exception) {
67
- print(e)
68
- ModuleException.START_ERROR.reject(
69
- promise,
70
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
71
- )
65
+ promise.reject(ModuleErrorCodes.START_ERROR, e.message, e)
72
66
  }
73
67
  }
74
68
 
@@ -77,19 +71,38 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
77
71
  * of ByteArray representing DER encoded X.509 certificates.
78
72
  * @param certificates - Two-dimensional array of base64 strings representing DER encoded X.509 certificate
79
73
  * @returns An ArrayList of ByteArray representing DER encoded X.509 certificates.
80
- * @throws ClassCastException if the element in the array is not a string
74
+ * @throws IllegalArgumentException if an element in the array is not base64 encoded
81
75
  */
82
76
  private fun parseCertificates(certificates: ReadableArray): List<List<ByteArray>> =
83
- (0 until certificates.size())
84
- .mapNotNull { chainIndex -> certificates.getArray(chainIndex) }
85
- .map { chain ->
86
- (0 until chain.size())
87
- .mapNotNull { certIndex ->
88
- chain.takeIf { it.getType(certIndex) == ReadableType.String }
89
- ?.getString(certIndex)
90
- ?.let { Base64Utils.decodeBase64(it) }
91
- }
77
+ /** Map the chain arrays and remove null entries. On each chain call the getArray method which
78
+ * can throw and if it does rethrow an exception with information on its position
79
+ */
80
+ (0 until certificates.size()).mapNotNull { chainIndex ->
81
+ val chain = runCatching { certificates.getArray(chainIndex) }
82
+ .getOrElse { throw IllegalArgumentException("Certificate chain at $chainIndex is not an array", it) }
83
+ ?: throw IllegalArgumentException("Certificate chain at index $chainIndex is null")
84
+
85
+ /**
86
+ * Map each chain certificate and remove null entries. On each certificate call the getString
87
+ * method which can throw and if it does rethrow an exception with information on its position
88
+ */
89
+ (0 until chain.size()).mapNotNull { certIndex ->
90
+ val base64 = runCatching { chain.getString(certIndex) }
91
+ .getOrElse { throw IllegalArgumentException("Failed to get certificate string at chain $chainIndex, cert $certIndex", it) }
92
+ ?: throw java.lang.IllegalArgumentException("Certificate at index $certIndex is null")
93
+
94
+ /**
95
+ * Decode the base64 string for each mapped certificate and if an error occurs rethrow
96
+ * an exception with information on its position
97
+ */
98
+ runCatching {
99
+ Base64Utils.decodeBase64(base64)
100
+ }.getOrElse {
101
+ throw IllegalArgumentException("Certificate at index $certIndex in the chain at index $chainIndex is not a valid base64 string", it)
102
+ }
92
103
  }
104
+ }
105
+
93
106
 
94
107
  /**
95
108
  * Creates a QR code to be scanned in order to initialize the presentation.
@@ -103,13 +116,10 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
103
116
  val qrCodeString = it.getQrCodeString()
104
117
  promise.resolve(qrCodeString)
105
118
  } ?: run {
106
- ModuleException.QR_ENGAGEMENT_NOT_DEFINED_ERROR.reject(promise)
119
+ promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED,NOT_INITIALIZED_ERROR_MESSAGE)
107
120
  }
108
121
  } catch (e: Exception) {
109
- ModuleException.GET_QR_CODE_ERROR.reject(
110
- promise,
111
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
112
- )
122
+ promise.reject(ModuleErrorCodes.GET_QR_CODE_ERROR, e.message, e)
113
123
  }
114
124
  }
115
125
 
@@ -125,10 +135,7 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
125
135
  deviceRetrievalHelper?.disconnect()
126
136
  promise.resolve(true)
127
137
  } catch (e: Exception) {
128
- ModuleException.CLOSE_ERROR.reject(
129
- promise,
130
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
131
- )
138
+ promise.reject(ModuleErrorCodes.CLOSE_ERROR, message=e.message, e)
132
139
  }
133
140
  }
134
141
 
@@ -150,19 +157,13 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
150
157
  it.sendErrorResponse(sessionDataStatus)
151
158
  promise.resolve(true)
152
159
  } else {
153
- ModuleException.SEND_ERROR_RESPONSE_ERROR.reject(
154
- promise,
155
- Pair(ERROR_KEY, "Invalid status code")
156
- )
160
+ promise.reject(ModuleErrorCodes.SEND_ERROR_RESPONSE_ERROR, message="Invalid status code")
157
161
  }
158
162
  } ?: run {
159
- ModuleException.QR_ENGAGEMENT_NOT_DEFINED_ERROR.reject(promise)
163
+ promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
160
164
  }
161
165
  } catch (e: Exception) {
162
- ModuleException.SEND_ERROR_RESPONSE_ERROR.reject(
163
- promise,
164
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
165
- )
166
+ promise.reject(ModuleErrorCodes.SEND_ERROR_RESPONSE_ERROR, message=e.message, e)
166
167
  }
167
168
  }
168
169
 
@@ -198,20 +199,14 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
198
199
  }
199
200
 
200
201
  override fun onError(message: String) {
201
- ModuleException.GENERATE_RESPONSE_ERROR.reject(
202
- promise,
203
- Pair(ERROR_KEY, message)
204
- )
202
+ promise.reject(ModuleErrorCodes.GENERATE_RESPONSE_ERROR, message)
205
203
  }
206
204
  })
207
205
  } ?: run {
208
- ModuleException.DRH_NOT_DEFINED.reject(promise)
206
+ promise.reject(ModuleErrorCodes.DRH_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
209
207
  }
210
208
  } catch (e: Exception) {
211
- ModuleException.GENERATE_RESPONSE_ERROR.reject(
212
- promise,
213
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
214
- )
209
+ promise.reject(ModuleErrorCodes.GENERATE_RESPONSE_ERROR, message=e.message, e)
215
210
  }
216
211
  }
217
212
 
@@ -231,13 +226,10 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
231
226
  promise.resolve(true)
232
227
  }
233
228
  ?: run {
234
- ModuleException.QR_ENGAGEMENT_NOT_DEFINED_ERROR.reject(promise)
229
+ promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
235
230
  }
236
231
  } catch (e: Exception) {
237
- ModuleException.SEND_RESPONSE_ERROR.reject(
238
- promise,
239
- Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
240
- )
232
+ promise.reject(ModuleErrorCodes.SEND_RESPONSE_ERROR, e.message, e)
241
233
  }
242
234
  }
243
235
 
@@ -247,32 +239,18 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
247
239
  mdocGeneratedNonce: String, documents: ReadableArray,
248
240
  fieldRequestedAndAccepted: String, promise: Promise
249
241
  ) {
250
- val sessionTranscript = try {
251
- OpenID4VP(
252
- clientId,
253
- responseUri,
254
- authorizationRequestNonce,
255
- mdocGeneratedNonce
256
- ).createSessionTranscript()
257
- } catch (e: Exception) {
258
- ModuleException.UNABLE_TO_GENERATE_TRANSCRIPT.reject(
259
- promise,
260
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
261
- )
262
- return
263
- }
242
+ try {
243
+ val sessionTranscript =
244
+ OpenID4VP(
245
+ clientId,
246
+ responseUri,
247
+ authorizationRequestNonce,
248
+ mdocGeneratedNonce
249
+ ).createSessionTranscript()
264
250
 
265
- val documentsParsed = try {
266
- parseDocRequested(documents)
267
- } catch (e: Exception) {
268
- ModuleException.INVALID_DOC_REQUESTED.reject(
269
- promise,
270
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
271
- )
272
- return
273
- }
251
+ val documentsParsed =
252
+ parseDocRequested(documents)
274
253
 
275
- try {
276
254
  val responseGenerator = ResponseGenerator(sessionTranscript)
277
255
  responseGenerator.createResponse(
278
256
  documentsParsed,
@@ -283,18 +261,12 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
283
261
  }
284
262
 
285
263
  override fun onError(message: String) {
286
- ModuleException.UNABLE_TO_GENERATE_RESPONSE.reject(
287
- promise,
288
- Pair(ERROR_USER_INFO_KEY, message)
289
- )
264
+ promise.reject(ModuleErrorCodes.GENERATE_OID4VP_RESPONSE_ERROR, message)
290
265
  }
291
266
  }
292
267
  )
293
268
  } catch (e: Exception) {
294
- ModuleException.UNABLE_TO_GENERATE_RESPONSE.reject(
295
- promise,
296
- Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
297
- )
269
+ promise.reject(ModuleErrorCodes.GENERATE_OID4VP_RESPONSE_ERROR, e.message, e)
298
270
  }
299
271
  }
300
272
 
@@ -350,28 +322,31 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
350
322
  * @throws IllegalArgumentException if the provided document doesn't adhere to the expected format
351
323
  */
352
324
  private fun parseDocRequested(documents: ReadableArray): Array<DocRequested> {
353
- return (0 until documents.size()).map { i ->
354
- val entry = documents.getMap(i)
355
- ?: throw IllegalArgumentException("Entry in ReadableArray is null")
356
-
357
- val alias = entry.getString("alias")
358
- val issuerSignedContentStr = entry.getString("issuerSignedContent")
359
- val docType = entry.getString("docType")
360
-
361
- if (
362
- alias == null || entry.getType("alias") != ReadableType.String ||
363
- issuerSignedContentStr == null || entry.getType("issuerSignedContent") != ReadableType.String ||
364
- docType == null || entry.getType("docType") != ReadableType.String
365
- ) throw IllegalArgumentException("Unable to decode the provided documents")
366
-
367
- val issuerSignedContent = Base64Utils.decodeBase64AndBase64Url(issuerSignedContentStr)
368
-
369
- DocRequested(
370
- issuerSignedContent,
371
- alias,
372
- docType
373
- )
374
- }.toTypedArray()
325
+ return try {
326
+ (0 until documents.size()).map { i ->
327
+ val entry = documents.getMap(i)
328
+ ?: throw IllegalArgumentException("Entry at index $i in ReadableArray is null")
329
+ val alias = entry.getString("alias")
330
+ val issuerSignedContentStr = entry.getString("issuerSignedContent")
331
+ val docType = entry.getString("docType")
332
+
333
+ if (
334
+ alias == null || entry.getType("alias") != ReadableType.String ||
335
+ issuerSignedContentStr == null || entry.getType("issuerSignedContent") != ReadableType.String ||
336
+ docType == null || entry.getType("docType") != ReadableType.String
337
+ ) throw IllegalArgumentException("Unable to decode the provided documents at index $i")
338
+
339
+ val issuerSignedContent = Base64Utils.decodeBase64AndBase64Url(issuerSignedContentStr)
340
+
341
+ DocRequested(
342
+ issuerSignedContent,
343
+ alias,
344
+ docType
345
+ )
346
+ }.toTypedArray()
347
+ } catch (e: Exception) {
348
+ throw IllegalArgumentException("Failed to parse documents: ${e.message}", e)
349
+ }
375
350
  }
376
351
 
377
352
  /**
@@ -398,44 +373,24 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
398
373
  */
399
374
  }
400
375
 
401
- private enum class ModuleException(
402
- val ex: Exception
403
- ) {
404
- /** ISO18013-5 related errors **/
405
- DRH_NOT_DEFINED(Exception("DRH_NOT_DEFINED")),
406
- QR_ENGAGEMENT_NOT_DEFINED_ERROR(Exception("QR_ENGAGEMENT_NOT_DEFINED_ERROR")),
407
- START_ERROR(Exception("START_ERROR")),
408
- GET_QR_CODE_ERROR(Exception("GET_QR_CODE_ERROR")),
409
- GENERATE_RESPONSE_ERROR(Exception("GENERATE_RESPONSE_ERROR")),
410
- SEND_RESPONSE_ERROR(Exception("SEND_RESPONSE_ERROR")),
411
- SEND_ERROR_RESPONSE_ERROR(Exception("SEND_ERROR_RESPONSE_ERROR")),
412
- CLOSE_ERROR(Exception("CLOSE_ERROR")),
413
-
414
- /** ISO18013-7 related errors **/
415
- UNABLE_TO_GENERATE_RESPONSE(Exception("UNABLE_TO_GENERATE_RESPONSE")),
416
- UNABLE_TO_GENERATE_TRANSCRIPT(Exception("UNABLE_TO_GENERATE_TRANSCRIPT")),
417
- INVALID_DOC_REQUESTED(Exception("INVALID_DOC_REQUESTED")),
418
- GENERATE_OID4VP_DEVICE_RESPONSE_FAILED(Exception("GENERATE_OID4VP_DEVICE_RESPONSE_FAILED"));
419
-
420
- fun reject(
421
- promise: Promise, vararg args: Pair<String, String>
422
- ) {
423
- exMap(*args).let {
424
- promise.reject(it.first, ex.message, it.second)
425
- }
426
- }
427
-
428
- private fun exMap(vararg args: Pair<String, String>): Pair<String, WritableMap> {
429
- val writableMap = WritableNativeMap()
430
- args.forEach { writableMap.putString(it.first, it.second) }
431
- return Pair(this.ex.message ?: "UNKNOWN", writableMap)
432
- }
433
- }
434
-
435
- private fun getExceptionMessageOrEmpty(e: Exception): String = e.message ?: ""
436
-
437
376
  companion object {
438
377
  const val NAME = "IoReactNativeIso18013"
439
- const val ERROR_KEY = "error"
378
+ const val NOT_INITIALIZED_ERROR_MESSAGE = "Resources not initialized properly, call the start method before this one."
379
+
380
+ // Errors which this module uses to reject a promise
381
+ private object ModuleErrorCodes {
382
+ // ISO18013-5 related errors
383
+ const val DRH_NOT_DEFINED = "DRH_NOT_DEFINED"
384
+ const val QR_ENGAGEMENT_NOT_DEFINED = "QR_ENGAGEMENT_NOT_DEFINED"
385
+ const val START_ERROR = "START_ERROR"
386
+ const val GET_QR_CODE_ERROR = "GET_QR_CODE_ERROR"
387
+ const val GENERATE_RESPONSE_ERROR = "GENERATE_RESPONSE_ERROR"
388
+ const val SEND_RESPONSE_ERROR = "SEND_RESPONSE_ERROR"
389
+ const val SEND_ERROR_RESPONSE_ERROR = "SEND_ERROR_RESPONSE_ERROR"
390
+ const val CLOSE_ERROR = "CLOSE_ERROR"
391
+
392
+ // ISO18013-7 related errors
393
+ const val GENERATE_OID4VP_RESPONSE_ERROR = "GENERATE_OID4VP_RESPONSE_ERROR"
394
+ }
440
395
  }
441
396
  }
@@ -4,8 +4,15 @@ import Foundation
4
4
  * This class contains a set of utilities for encoding and decoding base64 and base64url strings.
5
5
  */
6
6
  class Base64Utils {
7
- enum Base64DecodingError: Error {
8
- case invalidBase64(String)
7
+ enum Base64DecodingError: LocalizedError {
8
+ case invalidBase64
9
+
10
+ var errorDescription: String? {
11
+ switch self {
12
+ case .invalidBase64:
13
+ return "Input is not a valid Base64 or Base64URL string"
14
+ }
15
+ }
9
16
  }
10
17
 
11
18
  /**
@@ -24,7 +31,7 @@ class Base64Utils {
24
31
  } else if let data = Data(base64Encoded: base) {
25
32
  return data
26
33
  } else {
27
- throw Base64DecodingError.invalidBase64("Input is not valid Base64 or Base64URL")
34
+ throw Base64DecodingError.invalidBase64
28
35
  }
29
36
  }
30
37