@pagopa/io-react-native-iso18013 0.3.0 → 0.5.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/README.md +10 -5
- package/android/build.gradle +7 -1
- package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeCborModule.kt +69 -134
- package/android/src/main/java/com/ioreactnativeiso18013/IoReactNativeIso18013Module.kt +254 -183
- package/android/src/test/java/com/ioreactnativeiso18013/Base64UtilsTest.kt +66 -0
- package/android/src/test/java/com/ioreactnativeiso18013/IoReactNativeIso18013Test.kt +508 -0
- package/ios/Base64Utils.swift +10 -3
- package/ios/IoReactNativeCbor.mm +21 -21
- package/ios/IoReactNativeCbor.swift +116 -138
- package/ios/IoReactNativeIso18013.mm +10 -10
- package/ios/IoReactNativeIso18013.swift +172 -193
- package/lib/module/cbor/cbor/README.md +39 -59
- package/lib/module/cbor/cbor/decoder.js +13 -23
- package/lib/module/cbor/cbor/decoder.js.map +1 -1
- 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/cose/sign.js +4 -3
- package/lib/module/cbor/cose/sign.js.map +1 -1
- package/lib/module/iso18013/index.js +0 -1
- package/lib/module/iso18013/index.js.map +1 -1
- package/lib/module/iso18013/iso18013-5/README.md +120 -44
- 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 +20 -15
- package/lib/module/iso18013/iso18013-5/proximity.js.map +1 -1
- package/lib/module/iso18013/iso18013-5/{schema.js → request.js} +1 -37
- package/lib/module/iso18013/iso18013-5/request.js.map +1 -0
- package/lib/module/iso18013/iso18013-7/README.md +66 -41
- 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 +11 -24
- 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/decoder.d.ts +12 -22
- package/lib/typescript/src/cbor/cbor/decoder.d.ts.map +1 -1
- 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/cbor/cose/sign.d.ts +4 -3
- package/lib/typescript/src/cbor/cose/sign.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/index.d.ts +0 -1
- 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 +4 -3
- package/lib/typescript/src/iso18013/iso18013-5/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-5/proximity.d.ts +21 -14
- 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 -39
- 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 +3 -2
- package/lib/typescript/src/iso18013/iso18013-7/index.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts +11 -25
- package/lib/typescript/src/iso18013/iso18013-7/remote.d.ts.map +1 -1
- package/lib/typescript/src/iso18013/types.d.ts +25 -0
- package/lib/typescript/src/iso18013/types.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 +39 -59
- package/src/cbor/cbor/decoder.ts +13 -23
- 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/cose/sign.ts +5 -4
- package/src/iso18013/index.ts +0 -7
- package/src/iso18013/iso18013-5/README.md +120 -44
- package/src/iso18013/iso18013-5/error.ts +35 -0
- package/src/iso18013/iso18013-5/index.ts +9 -8
- package/src/iso18013/iso18013-5/proximity.ts +21 -17
- package/src/iso18013/iso18013-5/{schema.ts → request.ts} +0 -42
- package/src/iso18013/iso18013-7/README.md +66 -41
- package/src/iso18013/iso18013-7/error.ts +21 -0
- package/src/iso18013/iso18013-7/index.ts +6 -5
- package/src/iso18013/iso18013-7/remote.ts +12 -34
- package/src/iso18013/types.ts +24 -0
- 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
|
@@ -5,8 +5,6 @@ import React
|
|
|
5
5
|
@objc(IoReactNativeIso18013)
|
|
6
6
|
class IoReactNativeIso18013: RCTEventEmitter {
|
|
7
7
|
|
|
8
|
-
private typealias ME = ModuleException
|
|
9
|
-
|
|
10
8
|
@objc
|
|
11
9
|
override static func requiresMainQueueSetup() -> Bool {
|
|
12
10
|
return true
|
|
@@ -39,8 +37,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
39
37
|
|
|
40
38
|
/**
|
|
41
39
|
Starts the proximity flow by allocating the necessary resources and initializing the Bluetooth stack.
|
|
42
|
-
Resolves to true or rejects
|
|
43
|
-
|
|
40
|
+
Resolves to true or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
44
41
|
- Parameters:
|
|
45
42
|
- certificates: Two-dimensional array of base64 strings representing DER encoded X.509 certificate which are used to authenticate the verifier app
|
|
46
43
|
- resolve: The promise to be resolved
|
|
@@ -49,56 +46,60 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
49
46
|
@objc(start:withResolver:withRejecter:)
|
|
50
47
|
func start(
|
|
51
48
|
certificates: [Any],
|
|
52
|
-
|
|
49
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
53
50
|
reject: @escaping RCTPromiseRejectBlock
|
|
54
51
|
){
|
|
55
52
|
do {
|
|
56
|
-
let certsData = parseCertificates(certificates)
|
|
53
|
+
let certsData = try parseCertificates(certificates)
|
|
57
54
|
try Proximity.shared.start(certsData)
|
|
58
55
|
resolve(true)
|
|
59
|
-
} catch let
|
|
60
|
-
|
|
56
|
+
} catch let proximityError as ProximityError {
|
|
57
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, proximityError.description, proximityError)
|
|
58
|
+
} catch let parsingError as ParsingError{
|
|
59
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, parsingError.description, parsingError)
|
|
60
|
+
} catch {
|
|
61
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, error.localizedDescription, error)
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
Utility function to parse an array coming from the React Native Bridge into an array of Data representing DER encoded X.509 certificates.
|
|
66
|
-
|
|
67
67
|
- Parameters:
|
|
68
|
-
- certificates:Two-dimensional array of base64 strings representing DER encoded X.509 certificate
|
|
69
|
-
|
|
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
70
|
- Returns: A two-dimensional array of Data containing DER encoded X.509 certificates.
|
|
71
71
|
*/
|
|
72
|
-
private func parseCertificates(_ certificates:
|
|
73
|
-
return certificates.compactMap { item in
|
|
72
|
+
private func parseCertificates(_ certificates: [Any]) throws -> [[Data]] {
|
|
73
|
+
return try certificates.enumerated().compactMap { (chainIndex, item) in
|
|
74
74
|
guard let certStrings = item as? [String] else {
|
|
75
|
-
|
|
75
|
+
throw ParsingError.certificatesNotValid("Certificate chain at index \(chainIndex) is not an array of strings.")
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
return try certStrings.enumerated().map { (certIndex, certString) in
|
|
78
|
+
guard let data = Data(base64Encoded: certString) else {
|
|
79
|
+
throw ParsingError.certificatesNotValid("Certificate at index \(certIndex) in the chain at index \(chainIndex) is not a valid base64 string.")
|
|
80
|
+
}
|
|
81
|
+
return data
|
|
79
82
|
}
|
|
80
|
-
return dataArray.isEmpty ? nil : dataArray
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
Creates a QR code to be scanned in order to initialize the presentation.
|
|
86
|
-
Resolves with the QR code
|
|
87
|
-
|
|
88
|
+
Resolves with a string containing the QR code or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
88
89
|
- Parameters:
|
|
89
90
|
- resolve: The promise to be resolved
|
|
90
91
|
- reject: The promise to be rejected
|
|
91
|
-
|
|
92
|
+
*/
|
|
92
93
|
@objc(getQrCodeString:withRejecter:)
|
|
93
94
|
func getQrCodeString(
|
|
94
|
-
|
|
95
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
95
96
|
reject: @escaping RCTPromiseRejectBlock
|
|
96
97
|
) {
|
|
97
98
|
do{
|
|
98
99
|
let qrCodeString = try Proximity.shared.getQrCode()
|
|
99
100
|
resolve(qrCodeString)
|
|
100
|
-
} catch
|
|
101
|
-
|
|
101
|
+
} catch {
|
|
102
|
+
reject(ModuleErrorCodes.getQrCodeError.rawValue, error.localizedDescription, error)
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -106,33 +107,35 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
106
107
|
Parses an array of documents from the React Native bridge which doesn't have any typing to an array of ``IOWalletProximity.ProximityDocument``.
|
|
107
108
|
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.
|
|
108
109
|
The result can be fed to ``IOWalletProximity.generateResponse``.
|
|
109
|
-
|
|
110
110
|
- Parameters:
|
|
111
111
|
- documents: An array containing documents. Each document is defined as a map containing:
|
|
112
112
|
- issuerSignedContent which is a base64 or base64url encoded string representing the credential;
|
|
113
113
|
- alias which is the alias of the key used to sign the credential;
|
|
114
114
|
- docType which is the document type.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
- 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.
|
|
120
|
+
- Returns: An array of `ProximityDocument` containg the documents to be presented.
|
|
119
121
|
*/
|
|
120
122
|
private func parseDocuments(documents: [Any]) throws -> [ProximityDocument] {
|
|
121
|
-
return try documents.
|
|
122
|
-
guard let dict : [String: Any] = element as? [String: Any] else { throw
|
|
123
|
+
return try documents.map{ (element) -> ProximityDocument in
|
|
124
|
+
guard let dict : [String: Any] = element as? [String: Any] else { throw ParsingError.documentsNotValid("The provided documents array element can't be parsed as a dictionary") }
|
|
123
125
|
guard
|
|
124
126
|
let issuerSignedContent = dict["issuerSignedContent"] as? String,
|
|
125
127
|
let alias = dict["alias"] as? String,
|
|
126
128
|
let docType = dict["docType"] as? String,
|
|
127
129
|
let decodedIssuerSignedContent = try? Base64Utils.decodeBase64OrBase64URL(base: issuerSignedContent)
|
|
128
|
-
else {
|
|
129
|
-
|
|
130
|
+
else {
|
|
131
|
+
throw ParsingError.documentsNotValid ("The provided issuerSignedContent is not a valid base64 or base64url or don't contain the required fields alas, doctype and issuerSignedContent")
|
|
132
|
+
}
|
|
130
133
|
guard let document = ProximityDocument(
|
|
131
134
|
docType: docType,
|
|
132
135
|
issuerSigned: [UInt8](decodedIssuerSignedContent),
|
|
133
136
|
deviceKeyTag: alias
|
|
134
137
|
) else {
|
|
135
|
-
throw
|
|
138
|
+
throw ParsingError.documentsNotValid("The parsing for the document with docType \(docType) failed")
|
|
136
139
|
}
|
|
137
140
|
return document
|
|
138
141
|
}
|
|
@@ -142,72 +145,58 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
142
145
|
Parses a dictionary of accepted fields for the presentation from the React Native bridge which doesn't have any typing to a ``AcceptedFieldsDict`` dictionary.
|
|
143
146
|
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.
|
|
144
147
|
The result can be fed to ``IOWalletProximity.generateResponse``.
|
|
145
|
-
|
|
146
148
|
- Parameters:
|
|
147
|
-
- 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]]]
|
|
148
|
-
|
|
149
|
-
-
|
|
150
|
-
|
|
151
|
-
- Returns: An ``AcceptedFieldsDict`` containg the accepted fields to be presented
|
|
152
|
-
|
|
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]]].
|
|
150
|
+
- Throws: `ParsingError` if a value doesn't has the ``AcceptedFieldsDict`` shape or the result dictionary is empty.
|
|
151
|
+
- Returns: An ``AcceptedFieldsDict`` containg the accepted fields to be presented.
|
|
153
152
|
*/
|
|
154
|
-
private func parseAcceptedFields(acceptedFields: [AnyHashable: Any]) throws -> AcceptedFieldsDict {
|
|
153
|
+
private func parseAcceptedFields(acceptedFields: [AnyHashable: Any]) throws(ParsingError) -> AcceptedFieldsDict {
|
|
155
154
|
var result: AcceptedFieldsDict = [:]
|
|
156
155
|
|
|
157
156
|
for (key, value) in acceptedFields {
|
|
158
157
|
guard let keyString = key as? String else {
|
|
159
|
-
throw
|
|
158
|
+
throw ParsingError.acceptedFieldsNotValid("The accepted fields keys must be a String")
|
|
160
159
|
}
|
|
161
160
|
guard let valueDict = value as? [String: [String: Bool]] else {
|
|
162
|
-
throw
|
|
161
|
+
throw ParsingError.acceptedFieldsNotValid("The accepted fields values must be of type [String: [String: Bool]]")
|
|
163
162
|
}
|
|
164
163
|
result[keyString] = valueDict
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
if(result.isEmpty){
|
|
168
|
-
throw NSError(domain: "ParseAcceptedFields", code: -1, userInfo: [NSLocalizedDescriptionKey: "Error parsing documents"])
|
|
169
|
-
}
|
|
170
|
-
|
|
171
166
|
return result
|
|
172
167
|
}
|
|
173
168
|
|
|
174
169
|
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
- docType which is the document type.
|
|
185
|
-
- issuerSignedContent which is a base64 or base64url encoded string representing the credential;
|
|
186
|
-
- alias which is the alias of the key used to sign the credential;
|
|
187
|
-
- docType which is the document type.
|
|
188
|
-
- 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 credentia doctypel. 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:
|
|
170
|
+
Generates a response containing the documents and the fields which the user decided to present.
|
|
171
|
+
It parses the untyped ``documents`` and ``acceptedFields`` parameters and feeds them to the ``IOWalletProximity.generateDeviceResponse`` function.
|
|
172
|
+
Resolves with a base64 encoded response or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
173
|
+
- 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:
|
|
189
179
|
|
|
190
180
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
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,
|
|
199
188
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
- resolve: The promise to be resolved.
|
|
193
|
+
- reject: The promise to be rejected.
|
|
194
|
+
*/
|
|
206
195
|
@objc(generateResponse:withAcceptedFields:withResolver:withRejecter:)
|
|
207
196
|
func generateResponse(
|
|
208
197
|
documents: Array<Any>,
|
|
209
198
|
acceptedFields: [AnyHashable: Any],
|
|
210
|
-
|
|
199
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
211
200
|
reject: @escaping RCTPromiseRejectBlock
|
|
212
201
|
){
|
|
213
202
|
do {
|
|
@@ -216,16 +205,21 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
216
205
|
let deviceResponse = try Proximity.shared.generateDeviceResponse(items: items, documents: parsedDocuments, sessionTranscript: nil)
|
|
217
206
|
let strDeviceResponse = Data(deviceResponse).base64EncodedString()
|
|
218
207
|
resolve(strDeviceResponse)
|
|
219
|
-
}
|
|
220
|
-
|
|
208
|
+
}
|
|
209
|
+
catch let proximityError as ProximityError {
|
|
210
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, proximityError.description, proximityError)
|
|
211
|
+
}
|
|
212
|
+
catch let parsingError as ParsingError {
|
|
213
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, parsingError.description, parsingError)
|
|
214
|
+
} catch {
|
|
215
|
+
reject(ModuleErrorCodes.generateResponseError.rawValue, error.localizedDescription, error)
|
|
221
216
|
}
|
|
222
217
|
}
|
|
223
218
|
|
|
224
219
|
/**
|
|
225
220
|
Sends a response containing the documents and the fields which the user decided to present generated by ``generateResponse``.
|
|
226
|
-
It resolves to true after sending the response, otherwise it rejects if an error occurs while decoding the response.
|
|
227
221
|
Currently there's not evidence of the verifier app responding to this request, thus we don't handle the response.
|
|
228
|
-
|
|
222
|
+
Resolves with a true boolean in case of success or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
229
223
|
- Parameters:
|
|
230
224
|
- response: A base64 encoded string containing the response generated by ``generateResponse``
|
|
231
225
|
- resolve: The promise to be resolved
|
|
@@ -234,7 +228,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
234
228
|
@objc(sendResponse:withResolver:withRejecter:)
|
|
235
229
|
func sendResponse(
|
|
236
230
|
response: String,
|
|
237
|
-
|
|
231
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
238
232
|
reject: @escaping RCTPromiseRejectBlock
|
|
239
233
|
){
|
|
240
234
|
do{
|
|
@@ -244,50 +238,49 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
244
238
|
resolve(true)
|
|
245
239
|
}
|
|
246
240
|
}catch let error {
|
|
247
|
-
|
|
241
|
+
reject(ModuleErrorCodes.sendResponseError.rawValue, error.localizedDescription, error)
|
|
248
242
|
}
|
|
249
243
|
}
|
|
250
244
|
|
|
251
245
|
/**
|
|
252
246
|
Sends an error response during the presentation according to the SessionData status codes defined in table 20 of the ISO18013-5 standard.
|
|
247
|
+
Resolves to true or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
253
248
|
- Parameters:
|
|
254
|
-
-
|
|
249
|
+
- code: The status error to be sent is an integer of type ``SessionDataStatus``:
|
|
255
250
|
```
|
|
256
251
|
10 -> Error: session encryption
|
|
257
252
|
11 -> Error: CBOR decoding
|
|
258
253
|
20 -> Session termination
|
|
259
254
|
```
|
|
260
|
-
- resolve: The promise to be resolved
|
|
261
|
-
- reject: The promise to be rejected
|
|
255
|
+
- resolve: The promise to be resolved.
|
|
256
|
+
- reject: The promise to be rejected.
|
|
262
257
|
*/
|
|
263
258
|
@objc(sendErrorResponse:withResolver:withRejecter:)
|
|
264
|
-
func sendErrorResponse(
|
|
265
|
-
|
|
259
|
+
func sendErrorResponse(code: UInt64, _ resolve: @escaping RCTPromiseResolveBlock,
|
|
260
|
+
reject: @escaping RCTPromiseRejectBlock){
|
|
266
261
|
do{
|
|
267
|
-
if let statusEnum = SessionDataStatus(rawValue:
|
|
262
|
+
if let statusEnum = SessionDataStatus(rawValue: code) {
|
|
268
263
|
try Proximity.shared.errorPresentation(statusEnum)
|
|
269
264
|
} else {
|
|
270
|
-
|
|
265
|
+
reject(ModuleErrorCodes.sendErrorResponseError.rawValue, "Invalid status code provided: \(code)", nil)
|
|
271
266
|
}
|
|
272
267
|
resolve(true)
|
|
273
268
|
}catch let error{
|
|
274
|
-
|
|
269
|
+
reject(ModuleErrorCodes.sendErrorResponseError.rawValue, error.localizedDescription, error)
|
|
275
270
|
}
|
|
276
271
|
}
|
|
277
272
|
|
|
278
273
|
|
|
279
274
|
/**
|
|
280
275
|
Closes the bluetooth connection and clears any resource.
|
|
281
|
-
|
|
282
|
-
|
|
276
|
+
Resolves to true after closing the connection or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
283
277
|
- Parameters:
|
|
284
|
-
- resolve: The promise to be resolved
|
|
285
|
-
- reject: The promise to be rejected
|
|
286
|
-
|
|
278
|
+
- resolve: The promise to be resolved.
|
|
279
|
+
- reject: The promise to be rejected.
|
|
287
280
|
*/
|
|
288
281
|
@objc(close:withRejecter:)
|
|
289
282
|
func close(
|
|
290
|
-
|
|
283
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
291
284
|
reject: @escaping RCTPromiseRejectBlock
|
|
292
285
|
) {
|
|
293
286
|
Proximity.shared.stop()
|
|
@@ -295,13 +288,11 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
295
288
|
}
|
|
296
289
|
|
|
297
290
|
/**
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
- Returns: A JSON string representing the device request or nil if an error occurs
|
|
304
|
-
*/
|
|
291
|
+
Converts a device requested from the `onDocumentRequestReceived` callback into a serializable JSON.
|
|
292
|
+
- 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.
|
|
294
|
+
- Returns: A JSON string representing the device request or nil if an error occurs.
|
|
295
|
+
*/
|
|
305
296
|
private func deviceRequestToJson(request: [(docType: String, nameSpaces: [String: [String: Bool]], isAuthenticated: Bool)]?) -> String? {
|
|
306
297
|
var jsonRequest : [String: AnyHashable] = [:]
|
|
307
298
|
request?.forEach({
|
|
@@ -379,115 +370,103 @@ class IoReactNativeIso18013: RCTEventEmitter {
|
|
|
379
370
|
}
|
|
380
371
|
}
|
|
381
372
|
|
|
382
|
-
|
|
383
|
-
|
|
373
|
+
/**
|
|
374
|
+
Generates a CBOR encoded device response for ISO 18013-7 mDL remote presentation using OID4VP.
|
|
375
|
+
Resolves with the base64 encoded device response or rejects with an error code defined in ``ModuleErrorCodes``.
|
|
376
|
+
- 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.
|
|
403
|
+
*/
|
|
404
|
+
@objc(generateOID4VPDeviceResponse:withResponseUri:withAuthorizationRequestNonce:withMdocGeneratedNonce:withDocuments:withAcceptedFields:withResolver:withRejecter:)
|
|
405
|
+
func generateOID4VPDeviceResponse(
|
|
406
|
+
clientId: String,
|
|
384
407
|
responseUri: String,
|
|
385
408
|
authorizationRequestNonce: String,
|
|
386
409
|
mdocGeneratedNonce: String,
|
|
387
410
|
documents: [Any],
|
|
388
|
-
|
|
411
|
+
acceptedFields: [AnyHashable: Any],
|
|
389
412
|
resolver resolve: RCTPromiseResolveBlock,
|
|
390
413
|
rejecter reject: RCTPromiseRejectBlock
|
|
391
414
|
) {
|
|
392
415
|
|
|
393
416
|
do {
|
|
394
417
|
let sessionTranscript = Proximity.shared.generateOID4VPSessionTranscriptCBOR(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
418
|
+
clientId: clientId,
|
|
419
|
+
responseUri: responseUri,
|
|
420
|
+
authorizationRequestNonce: authorizationRequestNonce,
|
|
421
|
+
mdocGeneratedNonce: mdocGeneratedNonce
|
|
399
422
|
)
|
|
400
|
-
|
|
401
|
-
let documentsAsProximityDocument = try parseDocuments(documents: documents)
|
|
402
|
-
|
|
403
|
-
let items = try JSONDecoder().decode([String : [String : [String : Bool]]].self, from: Data(fieldRequestedAndAccepted.utf8))
|
|
404
423
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
ME.invalidDocRequested.reject(reject: reject)
|
|
414
|
-
} catch _ as DecodingError {
|
|
415
|
-
ME.invalidItems.reject(reject: reject)
|
|
416
|
-
} catch {
|
|
417
|
-
ME.unexpected.reject(reject: reject)
|
|
424
|
+
let documentsAsProximityDocument = try parseDocuments(documents: documents)
|
|
425
|
+
let items = try parseAcceptedFields(acceptedFields: acceptedFields)
|
|
426
|
+
let response = try Proximity.shared.generateDeviceResponse(items: items, documents: documentsAsProximityDocument, sessionTranscript: sessionTranscript)
|
|
427
|
+
resolve(Data(response).base64EncodedString())
|
|
428
|
+
} catch let parsingError as ParsingError{
|
|
429
|
+
reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, parsingError.description, parsingError)
|
|
430
|
+
} catch let proximityError as ProximityError{
|
|
431
|
+
reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, proximityError.description, proximityError)
|
|
418
432
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
private class DocRequested {
|
|
422
|
-
var issuerSignedContent : [UInt8]
|
|
423
|
-
var alias : String
|
|
424
|
-
var docType : String
|
|
425
|
-
|
|
426
|
-
public init(issuerSignedContent: [UInt8], alias: String, docType: String) {
|
|
427
|
-
self.issuerSignedContent = issuerSignedContent
|
|
428
|
-
self.alias = alias
|
|
429
|
-
self.docType = docType
|
|
433
|
+
catch {
|
|
434
|
+
reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, error.localizedDescription, error)
|
|
430
435
|
}
|
|
431
436
|
}
|
|
432
|
-
|
|
437
|
+
|
|
433
438
|
/**
|
|
434
|
-
|
|
435
|
-
|
|
439
|
+
Custom Error which is thrown when a parsing error occurs in our utility functions which converts data from the bridge to what
|
|
440
|
+
our underlying functions expect.
|
|
441
|
+
This is needed in order to provide a customized description which can include more debug information.
|
|
436
442
|
*/
|
|
437
|
-
|
|
443
|
+
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
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Errors which this module uses to reject a promise
|
|
461
|
+
private enum ModuleErrorCodes: String, CaseIterable {
|
|
462
|
+
// ISO18013-5 related errors
|
|
438
463
|
case startError = "START_ERROR"
|
|
439
|
-
case
|
|
440
|
-
case generateDeviceResponseError = "GENERATE_DEVICE_RESPONSE_ERROR"
|
|
464
|
+
case getQrCodeError = "GET_QR_CODE_ERROR"
|
|
441
465
|
case sendResponseError = "SEND_RESPONSE_ERROR"
|
|
442
|
-
case
|
|
443
|
-
case
|
|
444
|
-
case unableToGenerateResponse = "UNABLE_TO_GENERATE_RESPONSE_ERROR"
|
|
445
|
-
case invalidItems = "REQUESTED_ITEMS_PARSING_ERROR"
|
|
446
|
-
case unexpected = "UNEXPECTED_ERROR"
|
|
447
|
-
case unableToDecode = "UNABLE_TO_DECODE"
|
|
466
|
+
case sendErrorResponseError = "SEND_ERROR_RESPONSE_ERROR"
|
|
467
|
+
case generateResponseError = "GENERATE_RESPONSE_ERROR"
|
|
448
468
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
) -> NSError {
|
|
452
|
-
switch self {
|
|
453
|
-
case .startError:
|
|
454
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
455
|
-
case .qrCodeError:
|
|
456
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
457
|
-
case .generateDeviceResponseError:
|
|
458
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
459
|
-
case .sendResponseError:
|
|
460
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
461
|
-
case .sendErrorResponse:
|
|
462
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
463
|
-
case .invalidDocRequested:
|
|
464
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
465
|
-
case .unableToGenerateResponse:
|
|
466
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
467
|
-
case .invalidItems:
|
|
468
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
469
|
-
case .unexpected:
|
|
470
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
471
|
-
case .unableToDecode:
|
|
472
|
-
return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
Rejects the provided promise with the appropriate error message and additional data.
|
|
478
|
-
|
|
479
|
-
- Parameters:
|
|
480
|
-
- reject: the promise to be rejected.
|
|
481
|
-
- moreUserInfo: additional key-value pairs of data to be passed along with the error.
|
|
482
|
-
|
|
483
|
-
*/
|
|
484
|
-
func reject(
|
|
485
|
-
reject: RCTPromiseRejectBlock,
|
|
486
|
-
_ moreUserInfo: (String, Any)...
|
|
487
|
-
) {
|
|
488
|
-
let userInfo = [String: Any](uniqueKeysWithValues: moreUserInfo)
|
|
489
|
-
let error = error(userInfo: userInfo)
|
|
490
|
-
reject("\(error.code)", error.domain, error)
|
|
491
|
-
}
|
|
469
|
+
// ISO18013-7 related errors
|
|
470
|
+
case generateOID4VPResponseError = "GENERATE_OID4VP_RESPONSE_ERROR"
|
|
492
471
|
}
|
|
493
472
|
}
|