@pagopa/io-react-native-iso18013 0.2.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.
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeCborModule.kt +67 -164
- package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeIso18013Module.kt +102 -146
- package/ios/Base64Utils.swift +10 -3
- package/ios/IoReactNativeCbor.swift +55 -125
- package/ios/IoReactNativeIso18013.swift +88 -117
- package/lib/module/cbor/cbor/README.md +37 -5
- package/lib/module/cbor/cbor/error.js +15 -0
- package/lib/module/cbor/cbor/error.js.map +1 -0
- package/lib/module/cbor/cbor/index.js +1 -0
- package/lib/module/cbor/cbor/index.js.map +1 -1
- package/lib/module/cbor/cose/README.md +39 -9
- package/lib/module/cbor/cose/error.js +17 -0
- package/lib/module/cbor/cose/error.js.map +1 -0
- package/lib/module/cbor/cose/index.js +1 -0
- package/lib/module/cbor/cose/index.js.map +1 -1
- package/lib/module/cbor/index.js +1 -1
- package/lib/module/cbor/index.js.map +1 -1
- package/lib/module/iso18013/index.js +2 -3
- package/lib/module/iso18013/index.js.map +1 -1
- package/lib/module/iso18013/iso18013-5/README.md +52 -16
- package/lib/module/iso18013/iso18013-5/error.js +25 -0
- package/lib/module/iso18013/iso18013-5/error.js.map +1 -0
- package/lib/module/iso18013/iso18013-5/index.js +3 -2
- package/lib/module/iso18013/iso18013-5/index.js.map +1 -1
- package/lib/module/iso18013/iso18013-5/proximity.js +10 -18
- package/lib/module/iso18013/iso18013-5/proximity.js.map +1 -1
- package/lib/module/iso18013/iso18013-5/{schema.js → request.js} +1 -20
- package/lib/module/iso18013/iso18013-5/request.js.map +1 -0
- package/lib/module/iso18013/iso18013-7/README.md +38 -9
- package/lib/module/iso18013/iso18013-7/error.js +15 -0
- package/lib/module/iso18013/iso18013-7/error.js.map +1 -0
- package/lib/module/iso18013/iso18013-7/index.js +1 -0
- package/lib/module/iso18013/iso18013-7/index.js.map +1 -1
- package/lib/module/iso18013/iso18013-7/remote.js +2 -14
- package/lib/module/iso18013/iso18013-7/remote.js.map +1 -1
- package/lib/module/schema.js +46 -0
- package/lib/module/schema.js.map +1 -0
- package/lib/typescript/src/cbor/cbor/error.d.ts +68 -0
- package/lib/typescript/src/cbor/cbor/error.d.ts.map +1 -0
- package/lib/typescript/src/cbor/cbor/index.d.ts +1 -1
- package/lib/typescript/src/cbor/cbor/index.d.ts.map +1 -1
- package/lib/typescript/src/cbor/cose/error.d.ts +68 -0
- package/lib/typescript/src/cbor/cose/error.d.ts.map +1 -0
- package/lib/typescript/src/cbor/cose/index.d.ts +1 -1
- package/lib/typescript/src/cbor/cose/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/index.d.ts +1 -2
- package/lib/typescript/src/iso18013/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-5/error.d.ts +73 -0
- package/lib/typescript/src/iso18013/iso18013-5/error.d.ts.map +1 -0
- package/lib/typescript/src/iso18013/iso18013-5/index.d.ts +3 -2
- package/lib/typescript/src/iso18013/iso18013-5/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts +2 -7
- package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-5/{schema.d.ts → request.d.ts} +1 -16
- package/lib/typescript/src/iso18013/iso18013-5/request.d.ts.map +1 -0
- package/lib/typescript/src/iso18013/iso18013-7/error.d.ts +68 -0
- package/lib/typescript/src/iso18013/iso18013-7/error.d.ts.map +1 -0
- package/lib/typescript/src/iso18013/iso18013-7/index.d.ts +2 -1
- package/lib/typescript/src/iso18013/iso18013-7/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts +0 -14
- package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts.map +1 -1
- package/lib/typescript/src/schema.d.ts +76 -0
- package/lib/typescript/src/schema.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/cbor/cbor/README.md +37 -5
- package/src/cbor/cbor/error.ts +23 -0
- package/src/cbor/cbor/index.ts +5 -1
- package/src/cbor/cose/README.md +39 -9
- package/src/cbor/cose/error.ts +23 -0
- package/src/cbor/cose/index.ts +5 -1
- package/src/cbor/index.ts +1 -1
- package/src/iso18013/index.ts +2 -9
- package/src/iso18013/iso18013-5/README.md +52 -16
- package/src/iso18013/iso18013-5/error.ts +35 -0
- package/src/iso18013/iso18013-5/index.ts +9 -4
- package/src/iso18013/iso18013-5/proximity.ts +11 -19
- package/src/iso18013/iso18013-5/{schema.ts → request.ts} +0 -20
- package/src/iso18013/iso18013-7/README.md +38 -9
- package/src/iso18013/iso18013-7/error.ts +21 -0
- package/src/iso18013/iso18013-7/index.ts +5 -4
- package/src/iso18013/iso18013-7/remote.ts +2 -22
- package/src/schema.ts +55 -0
- package/lib/module/cbor/cbor/failure.js +0 -2
- package/lib/module/cbor/cbor/failure.js.map +0 -1
- package/lib/module/cbor/cose/failure.js +0 -2
- package/lib/module/cbor/cose/failure.js.map +0 -1
- package/lib/module/iso18013/iso18013-5/schema.js.map +0 -1
- package/lib/typescript/src/cbor/cbor/failure.d.ts +0 -15
- package/lib/typescript/src/cbor/cbor/failure.d.ts.map +0 -1
- package/lib/typescript/src/cbor/cose/failure.d.ts +0 -15
- package/lib/typescript/src/cbor/cose/failure.d.ts.map +0 -1
- package/lib/typescript/src/iso18013/iso18013-5/schema.d.ts.map +0 -1
- package/src/cbor/cbor/failure.ts +0 -18
- package/src/cbor/cose/failure.ts +0 -20
package/android/build.gradle
CHANGED
|
@@ -77,7 +77,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
77
77
|
dependencies {
|
|
78
78
|
implementation "com.facebook.react:react-android"
|
|
79
79
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
80
|
-
implementation "it.pagopa.io.wallet.proximity:proximity:2.
|
|
80
|
+
implementation "it.pagopa.io.wallet.proximity:proximity:2.1.0"
|
|
81
81
|
implementation "it.pagopa.io.wallet.cbor:cbor:1.3.0"
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
promise
|
|
176
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
@@ -36,7 +34,7 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
36
34
|
* @param peripheralMode - Whether the device is in peripheral mode. Defaults to true
|
|
37
35
|
* @param centralClientMode - Whether the device is in central client mode. Defaults to false
|
|
38
36
|
* @param clearBleCache - Whether the BLE cache should be cleared. Defaults to true
|
|
39
|
-
* @param certificates -
|
|
37
|
+
* @param certificates - Two-dimensional array of base64 strings representing DER encoded X.509 certificate which are used to authenticate the verifier app
|
|
40
38
|
* @param promise - The promise which will be resolved in case of success or rejected in case of failure.
|
|
41
39
|
*/
|
|
42
40
|
@ReactMethod
|
|
@@ -54,41 +52,57 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
54
52
|
clearBleCache = clearBleCache
|
|
55
53
|
)
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
val certificatesList = parseCertificates(certificates)
|
|
58
56
|
qrEngagement = QrEngagement.build(reactApplicationContext, listOf(retrievalMethod)).apply {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
if (certificatesList.isNotEmpty()) {
|
|
58
|
+
withReaderTrustStore(certificatesList)
|
|
59
|
+
}
|
|
62
60
|
}
|
|
63
61
|
qrEngagement?.configure()
|
|
64
62
|
setupProximityHandler()
|
|
65
63
|
promise.resolve(true)
|
|
66
64
|
} catch (e: Exception) {
|
|
67
|
-
|
|
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
|
|
|
75
69
|
/**
|
|
76
70
|
* Utility function to parse an array coming from the React Native Bridge into an ArrayList
|
|
77
71
|
* of ByteArray representing DER encoded X.509 certificates.
|
|
78
|
-
* @param certificates -
|
|
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
|
|
74
|
+
* @throws IllegalArgumentException if an element in the array is not base64 encoded
|
|
81
75
|
*/
|
|
76
|
+
private fun parseCertificates(certificates: ReadableArray): List<List<ByteArray>> =
|
|
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
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
82
105
|
|
|
83
|
-
// private fun parseCertificates(certificates: ReadableArray): ArrayList<ByteArray> {
|
|
84
|
-
// return ArrayList(
|
|
85
|
-
// (0 until certificates.size()).mapNotNull { i ->
|
|
86
|
-
// certificates.getString(i)?.let { cert ->
|
|
87
|
-
// Base64Utils.decodeBase64(cert)
|
|
88
|
-
// }
|
|
89
|
-
// }
|
|
90
|
-
// )
|
|
91
|
-
// }
|
|
92
106
|
|
|
93
107
|
/**
|
|
94
108
|
* Creates a QR code to be scanned in order to initialize the presentation.
|
|
@@ -102,13 +116,10 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
102
116
|
val qrCodeString = it.getQrCodeString()
|
|
103
117
|
promise.resolve(qrCodeString)
|
|
104
118
|
} ?: run {
|
|
105
|
-
|
|
119
|
+
promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED,NOT_INITIALIZED_ERROR_MESSAGE)
|
|
106
120
|
}
|
|
107
121
|
} catch (e: Exception) {
|
|
108
|
-
|
|
109
|
-
promise,
|
|
110
|
-
Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
|
|
111
|
-
)
|
|
122
|
+
promise.reject(ModuleErrorCodes.GET_QR_CODE_ERROR, e.message, e)
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
|
|
@@ -124,10 +135,7 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
124
135
|
deviceRetrievalHelper?.disconnect()
|
|
125
136
|
promise.resolve(true)
|
|
126
137
|
} catch (e: Exception) {
|
|
127
|
-
|
|
128
|
-
promise,
|
|
129
|
-
Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
|
|
130
|
-
)
|
|
138
|
+
promise.reject(ModuleErrorCodes.CLOSE_ERROR, message=e.message, e)
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
141
|
|
|
@@ -149,19 +157,13 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
149
157
|
it.sendErrorResponse(sessionDataStatus)
|
|
150
158
|
promise.resolve(true)
|
|
151
159
|
} else {
|
|
152
|
-
|
|
153
|
-
promise,
|
|
154
|
-
Pair(ERROR_KEY, "Invalid status code")
|
|
155
|
-
)
|
|
160
|
+
promise.reject(ModuleErrorCodes.SEND_ERROR_RESPONSE_ERROR, message="Invalid status code")
|
|
156
161
|
}
|
|
157
162
|
} ?: run {
|
|
158
|
-
|
|
163
|
+
promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
|
|
159
164
|
}
|
|
160
165
|
} catch (e: Exception) {
|
|
161
|
-
|
|
162
|
-
promise,
|
|
163
|
-
Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
|
|
164
|
-
)
|
|
166
|
+
promise.reject(ModuleErrorCodes.SEND_ERROR_RESPONSE_ERROR, message=e.message, e)
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -197,20 +199,14 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
override fun onError(message: String) {
|
|
200
|
-
|
|
201
|
-
promise,
|
|
202
|
-
Pair(ERROR_KEY, message)
|
|
203
|
-
)
|
|
202
|
+
promise.reject(ModuleErrorCodes.GENERATE_RESPONSE_ERROR, message)
|
|
204
203
|
}
|
|
205
204
|
})
|
|
206
205
|
} ?: run {
|
|
207
|
-
|
|
206
|
+
promise.reject(ModuleErrorCodes.DRH_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
|
|
208
207
|
}
|
|
209
208
|
} catch (e: Exception) {
|
|
210
|
-
|
|
211
|
-
promise,
|
|
212
|
-
Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
|
|
213
|
-
)
|
|
209
|
+
promise.reject(ModuleErrorCodes.GENERATE_RESPONSE_ERROR, message=e.message, e)
|
|
214
210
|
}
|
|
215
211
|
}
|
|
216
212
|
|
|
@@ -230,13 +226,10 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
230
226
|
promise.resolve(true)
|
|
231
227
|
}
|
|
232
228
|
?: run {
|
|
233
|
-
|
|
229
|
+
promise.reject(ModuleErrorCodes.QR_ENGAGEMENT_NOT_DEFINED, message=NOT_INITIALIZED_ERROR_MESSAGE)
|
|
234
230
|
}
|
|
235
231
|
} catch (e: Exception) {
|
|
236
|
-
|
|
237
|
-
promise,
|
|
238
|
-
Pair(ERROR_KEY, getExceptionMessageOrEmpty(e))
|
|
239
|
-
)
|
|
232
|
+
promise.reject(ModuleErrorCodes.SEND_RESPONSE_ERROR, e.message, e)
|
|
240
233
|
}
|
|
241
234
|
}
|
|
242
235
|
|
|
@@ -246,32 +239,18 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
246
239
|
mdocGeneratedNonce: String, documents: ReadableArray,
|
|
247
240
|
fieldRequestedAndAccepted: String, promise: Promise
|
|
248
241
|
) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
ModuleException.UNABLE_TO_GENERATE_TRANSCRIPT.reject(
|
|
258
|
-
promise,
|
|
259
|
-
Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
|
|
260
|
-
)
|
|
261
|
-
return
|
|
262
|
-
}
|
|
242
|
+
try {
|
|
243
|
+
val sessionTranscript =
|
|
244
|
+
OpenID4VP(
|
|
245
|
+
clientId,
|
|
246
|
+
responseUri,
|
|
247
|
+
authorizationRequestNonce,
|
|
248
|
+
mdocGeneratedNonce
|
|
249
|
+
).createSessionTranscript()
|
|
263
250
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} catch (e: Exception) {
|
|
267
|
-
ModuleException.INVALID_DOC_REQUESTED.reject(
|
|
268
|
-
promise,
|
|
269
|
-
Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
|
|
270
|
-
)
|
|
271
|
-
return
|
|
272
|
-
}
|
|
251
|
+
val documentsParsed =
|
|
252
|
+
parseDocRequested(documents)
|
|
273
253
|
|
|
274
|
-
try {
|
|
275
254
|
val responseGenerator = ResponseGenerator(sessionTranscript)
|
|
276
255
|
responseGenerator.createResponse(
|
|
277
256
|
documentsParsed,
|
|
@@ -282,18 +261,12 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
282
261
|
}
|
|
283
262
|
|
|
284
263
|
override fun onError(message: String) {
|
|
285
|
-
|
|
286
|
-
promise,
|
|
287
|
-
Pair(ERROR_USER_INFO_KEY, message)
|
|
288
|
-
)
|
|
264
|
+
promise.reject(ModuleErrorCodes.GENERATE_OID4VP_RESPONSE_ERROR, message)
|
|
289
265
|
}
|
|
290
266
|
}
|
|
291
267
|
)
|
|
292
268
|
} catch (e: Exception) {
|
|
293
|
-
|
|
294
|
-
promise,
|
|
295
|
-
Pair(ERROR_USER_INFO_KEY, e.message.orEmpty())
|
|
296
|
-
)
|
|
269
|
+
promise.reject(ModuleErrorCodes.GENERATE_OID4VP_RESPONSE_ERROR, e.message, e)
|
|
297
270
|
}
|
|
298
271
|
}
|
|
299
272
|
|
|
@@ -349,28 +322,31 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
349
322
|
* @throws IllegalArgumentException if the provided document doesn't adhere to the expected format
|
|
350
323
|
*/
|
|
351
324
|
private fun parseDocRequested(documents: ReadableArray): Array<DocRequested> {
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
+
}
|
|
374
350
|
}
|
|
375
351
|
|
|
376
352
|
/**
|
|
@@ -397,44 +373,24 @@ class IoReactNativeIso18013Module(reactContext: ReactApplicationContext) :
|
|
|
397
373
|
*/
|
|
398
374
|
}
|
|
399
375
|
|
|
400
|
-
private enum class ModuleException(
|
|
401
|
-
val ex: Exception
|
|
402
|
-
) {
|
|
403
|
-
/** ISO18013-5 related errors **/
|
|
404
|
-
DRH_NOT_DEFINED(Exception("DRH_NOT_DEFINED")),
|
|
405
|
-
QR_ENGAGEMENT_NOT_DEFINED_ERROR(Exception("QR_ENGAGEMENT_NOT_DEFINED_ERROR")),
|
|
406
|
-
START_ERROR(Exception("START_ERROR")),
|
|
407
|
-
GET_QR_CODE_ERROR(Exception("GET_QR_CODE_ERROR")),
|
|
408
|
-
GENERATE_RESPONSE_ERROR(Exception("GENERATE_RESPONSE_ERROR")),
|
|
409
|
-
SEND_RESPONSE_ERROR(Exception("SEND_RESPONSE_ERROR")),
|
|
410
|
-
SEND_ERROR_RESPONSE_ERROR(Exception("SEND_ERROR_RESPONSE_ERROR")),
|
|
411
|
-
CLOSE_ERROR(Exception("CLOSE_ERROR")),
|
|
412
|
-
|
|
413
|
-
/** ISO18013-7 related errors **/
|
|
414
|
-
UNABLE_TO_GENERATE_RESPONSE(Exception("UNABLE_TO_GENERATE_RESPONSE")),
|
|
415
|
-
UNABLE_TO_GENERATE_TRANSCRIPT(Exception("UNABLE_TO_GENERATE_TRANSCRIPT")),
|
|
416
|
-
INVALID_DOC_REQUESTED(Exception("INVALID_DOC_REQUESTED")),
|
|
417
|
-
GENERATE_OID4VP_DEVICE_RESPONSE_FAILED(Exception("GENERATE_OID4VP_DEVICE_RESPONSE_FAILED"));
|
|
418
|
-
|
|
419
|
-
fun reject(
|
|
420
|
-
promise: Promise, vararg args: Pair<String, String>
|
|
421
|
-
) {
|
|
422
|
-
exMap(*args).let {
|
|
423
|
-
promise.reject(it.first, ex.message, it.second)
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
private fun exMap(vararg args: Pair<String, String>): Pair<String, WritableMap> {
|
|
428
|
-
val writableMap = WritableNativeMap()
|
|
429
|
-
args.forEach { writableMap.putString(it.first, it.second) }
|
|
430
|
-
return Pair(this.ex.message ?: "UNKNOWN", writableMap)
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
private fun getExceptionMessageOrEmpty(e: Exception): String = e.message ?: ""
|
|
435
|
-
|
|
436
376
|
companion object {
|
|
437
377
|
const val NAME = "IoReactNativeIso18013"
|
|
438
|
-
const val
|
|
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
|
+
}
|
|
439
395
|
}
|
|
440
396
|
}
|
package/ios/Base64Utils.swift
CHANGED
|
@@ -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:
|
|
8
|
-
|
|
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
|
|
34
|
+
throw Base64DecodingError.invalidBase64
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
|