@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
@@ -4,44 +4,42 @@ import CryptoKit
4
4
 
5
5
  @objc(IoReactNativeCbor)
6
6
  class IoReactNativeCbor: NSObject {
7
- private typealias ME = ModuleException
8
- private let keyConfig: KeyConfig = .ec
9
7
 
10
8
  @objc func decode(
11
9
  _ cbor: String,
12
10
  resolver resolve: RCTPromiseResolveBlock,
13
11
  rejecter reject: RCTPromiseRejectBlock
14
12
  ) {
15
- guard let data = try? Base64Utils.decodeBase64OrBase64URL(base: cbor) else {
16
- ME.invalidEncoding.reject(reject: reject)
17
- return
18
- }
19
-
20
- guard let json = CborCose.jsonFromCBOR(data: data) else {
21
- ME.unableToDecode.reject(reject: reject)
22
- return
13
+ do{
14
+ let data = try Base64Utils.decodeBase64OrBase64URL(base: cbor)
15
+ guard let json = CborCose.jsonFromCBOR(data: data) else {
16
+ // We don't have the exact error here as this method returns nil upon failure
17
+ reject(ModuleErrorCodes.decodeError.rawValue, "Unable to decode CBOR", nil)
18
+ return
19
+ }
20
+ resolve(json);
21
+
22
+ }catch{
23
+ reject(ModuleErrorCodes.decodeError.rawValue, error.localizedDescription, error)
23
24
  }
24
-
25
- resolve(json);
26
25
  }
27
26
 
28
-
29
27
  @objc func decodeDocuments(
30
28
  _ mdoc: String,
31
29
  resolver resolve: RCTPromiseResolveBlock,
32
30
  rejecter reject: RCTPromiseRejectBlock
33
31
  ) {
34
- guard let data = try? Base64Utils.decodeBase64OrBase64URL(base: mdoc) else {
35
- ME.invalidEncoding.reject(reject: reject)
32
+ do{
33
+ let data = try Base64Utils.decodeBase64OrBase64URL(base: mdoc)
34
+ guard let json = CborCose.decodeCBOR(data: data, true, true) else {
35
+ // We don't have the exact error here as this method returns nil upon failure
36
+ reject(ModuleErrorCodes.decodeDocumentsError.rawValue, "Unable to decode document CBOR", nil)
36
37
  return
38
+ }
39
+ resolve(json);
40
+ }catch{
41
+ reject(ModuleErrorCodes.decodeDocumentsError.rawValue, error.localizedDescription, error)
37
42
  }
38
-
39
- guard let json = CborCose.decodeCBOR(data: data, true, true) else {
40
- ME.unableToDecode.reject(reject: reject)
41
- return
42
- }
43
-
44
- resolve(json);
45
43
  }
46
44
 
47
45
  @objc func decodeIssuerSigned(
@@ -49,17 +47,17 @@ class IoReactNativeCbor: NSObject {
49
47
  resolver resolve: RCTPromiseResolveBlock,
50
48
  rejecter reject: RCTPromiseRejectBlock
51
49
  ) {
52
- guard let data = try? Base64Utils.decodeBase64OrBase64URL(base: issuerSigned) else {
53
- ME.invalidEncoding.reject(reject: reject)
50
+ do{
51
+ let data = try Base64Utils.decodeBase64OrBase64URL(base: issuerSigned)
52
+ guard let json = CborCose.issuerSignedCborToJson(data: data) else {
53
+ // We don't have the exact error here as this method returns nil upon failure
54
+ reject(ModuleErrorCodes.decodeIssuerSignedError.rawValue, "Unable to decode Issuer Signed CBOR", nil)
54
55
  return
56
+ }
57
+ resolve(json);
58
+ }catch{
59
+ reject(ModuleErrorCodes.decodeIssuerSignedError.rawValue, error.localizedDescription, error)
55
60
  }
56
-
57
- guard let json = CborCose.issuerSignedCborToJson(data: data) else {
58
- ME.unableToDecode.reject(reject: reject)
59
- return
60
- }
61
-
62
- resolve(json);
63
61
  }
64
62
 
65
63
  @objc func sign(
@@ -68,24 +66,25 @@ class IoReactNativeCbor: NSObject {
68
66
  resolver resolve: @escaping RCTPromiseResolveBlock,
69
67
  rejecter reject: @escaping RCTPromiseRejectBlock
70
68
  ) {
71
- DispatchQueue.global().async { [weak self] in
72
- guard self != nil else {
73
- ME.threadingError.reject(reject: reject)
74
- return
69
+ DispatchQueue.global().async { [weak self] in
70
+ guard self != nil else {
71
+ reject(ModuleErrorCodes.threadingError.rawValue, "Failed to perform background operation, self was deallocated", nil)
72
+ return
73
+ }
74
+
75
+ do{
76
+ let data = try Base64Utils.decodeBase64OrBase64URL(base: payloadData)
77
+ guard let coseKey = CoseKeyPrivate(crv: .p256, keyTag: keyTag) else {
78
+ // We don't have the exact error here as this method returns nil upon failure
79
+ reject(ModuleErrorCodes.signError.rawValue, "Unable to create a private key with the given keytag: \(keyTag)", nil)
80
+ return
81
+ }
82
+ let signedPayload = CborCose.sign(data: data, privateKey: coseKey)
83
+ resolve(signedPayload.base64EncodedString())
75
84
  }
76
-
77
- guard let data = try? Base64Utils.decodeBase64OrBase64URL(base: payloadData) else {
78
- ME.invalidEncoding.reject(reject: reject)
79
- return
85
+ catch{
86
+ reject(ModuleErrorCodes.signError.rawValue, error.localizedDescription, error)
80
87
  }
81
-
82
- guard let coseKey = CoseKeyPrivate(crv: .p256, keyTag: keyTag) else {
83
- ME.publicKeyNotFound.reject(reject: reject)
84
- return
85
- }
86
-
87
- let signedPayload = CborCose.sign(data: data, privateKey: coseKey)
88
- resolve(signedPayload.base64EncodedString())
89
88
  }
90
89
  }
91
90
 
@@ -96,93 +95,24 @@ class IoReactNativeCbor: NSObject {
96
95
  rejecter reject: RCTPromiseRejectBlock
97
96
  ) {
98
97
  do {
99
- guard let data = try? Base64Utils.decodeBase64OrBase64URL(base: sign1Data) else {
100
- ME.invalidEncoding.reject(reject: reject)
101
- return
102
- }
103
-
98
+ let data = try Base64Utils.decodeBase64OrBase64URL(base: sign1Data)
104
99
  let publicKeyJson = try JSONSerialization.data(withJSONObject: jwk, options:[] )
105
100
  let publicKeyString = String(data: publicKeyJson, encoding: .utf8)!
106
101
  let publicKey = CoseKey(jwk: publicKeyString)!
107
-
108
102
  let verified = CborCose.verify(data: data, publicKey: publicKey)
109
-
110
103
  resolve(verified)
111
104
  } catch {
112
- ME.unknownException.reject(reject: reject)
105
+ reject(ModuleErrorCodes.verifyError.rawValue, error.localizedDescription, error)
113
106
  }
114
107
  }
115
-
116
- private func keyExists(keyTag: String) -> (key: SecKey?, status: OSStatus) {
117
- let getQuery = privateKeyKeychainQuery(keyTag: keyTag)
118
- var item: CFTypeRef?
119
- let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
120
- return (status == errSecSuccess ? (item as! SecKey) : nil, status)
121
- }
122
108
 
123
- private func privateKeyKeychainQuery(
124
- keyTag: String
125
- ) -> [String : Any] {
126
- return [
127
- kSecClass as String: kSecClassKey,
128
- kSecAttrApplicationTag as String: keyTag,
129
- kSecAttrKeyType as String: keyConfig.keyType(),
130
- kSecReturnRef as String: true
131
- ]
132
- }
133
-
134
- private enum KeyConfig: Int, CaseIterable {
135
- case ec
136
-
137
- func keyType() -> CFString {
138
- switch self {
139
- case .ec:
140
- return kSecAttrKeyTypeECSECPrimeRandom
141
- }
142
- }
143
-
144
- func keySizeInBits() -> Int {
145
- switch self {
146
- case .ec:
147
- return 256
148
- }
149
- }
150
-
151
- func keySignAlgorithm() -> SecKeyAlgorithm {
152
- switch self {
153
- case .ec:
154
- return .ecdsaSignatureMessageX962SHA256
155
- }
156
- }
157
- }
158
-
159
- private enum ModuleException: String, CaseIterable {
160
- case unableToDecode = "UNABLE_TO_DECODE"
161
- case publicKeyNotFound = "PUBLIC_KEY_NOT_FOUND"
162
- case invalidEncoding = "INVALID_ENCODING"
109
+ // Errors which this module uses to reject a promise
110
+ private enum ModuleErrorCodes: String, CaseIterable {
111
+ case decodeError = "DECODE_ERROR"
112
+ case decodeDocumentsError = "DECODE_DOCUMENTS_ERROR"
113
+ case decodeIssuerSignedError = "DECODE_ISSUER_SIGNED_ERROR"
114
+ case signError = "SIGN_ERROR"
115
+ case verifyError = "VERIFY_ERROR"
163
116
  case threadingError = "THREADING_ERROR"
164
- case unknownException = "UNKNOWN_EXCEPTION"
165
-
166
- func error(userInfo: [String : Any]? = nil) -> NSError {
167
- switch self {
168
- case .unableToDecode:
169
- return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
170
- case .publicKeyNotFound:
171
- return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
172
- case .invalidEncoding:
173
- return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
174
- case .threadingError:
175
- return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
176
- case .unknownException:
177
- return NSError(domain: self.rawValue, code: -1, userInfo: userInfo)
178
- }
179
- }
180
-
181
- func reject(reject: RCTPromiseRejectBlock, _ moreUserInfo: (String, Any)...) {
182
- var userInfo = [String : Any]()
183
- moreUserInfo.forEach { userInfo[$0.0] = $0.1 }
184
- let error = error(userInfo: userInfo)
185
- reject("\(error.code)", error.domain, error)
186
- }
187
117
  }
188
118
  }
@@ -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
@@ -53,11 +51,15 @@ class IoReactNativeIso18013: RCTEventEmitter {
53
51
  reject: @escaping RCTPromiseRejectBlock
54
52
  ){
55
53
  do {
56
- let certsData = parseCertificates(certificates)
54
+ let certsData = try parseCertificates(certificates)
57
55
  try Proximity.shared.start(certsData)
58
56
  resolve(true)
59
- } catch let error {
60
- ME.startError.reject(reject: reject, ("error", error.localizedDescription))
57
+ } catch let proximityError as ProximityError {
58
+ reject(ModuleErrorCodes.generateResponseError.rawValue, proximityError.description, proximityError)
59
+ } catch let parsingError as ParsingError{
60
+ reject(ModuleErrorCodes.generateResponseError.rawValue, parsingError.description, parsingError)
61
+ } catch {
62
+ reject(ModuleErrorCodes.generateResponseError.rawValue, error.localizedDescription, error)
61
63
  }
62
64
  }
63
65
 
@@ -69,15 +71,17 @@ class IoReactNativeIso18013: RCTEventEmitter {
69
71
 
70
72
  - Returns: A two-dimensional array of Data containing DER encoded X.509 certificates.
71
73
  */
72
- private func parseCertificates(_ certificates: Array<Any>) -> [[Data]] {
73
- return certificates.compactMap { item in
74
+ private func parseCertificates(_ certificates: [Any]) throws -> [[Data]] {
75
+ return try certificates.enumerated().compactMap { (chainIndex, item) in
74
76
  guard let certStrings = item as? [String] else {
75
- return nil
77
+ throw ParsingError.certificatesNotValid("Certificate chain at index \(chainIndex) is not an array of strings.")
76
78
  }
77
- let dataArray = certStrings.compactMap { certString in
78
- Data(base64Encoded: certString)
79
+ return try certStrings.enumerated().map { (certIndex, certString) in
80
+ guard let data = Data(base64Encoded: certString) else {
81
+ throw ParsingError.certificatesNotValid("Certificate at index \(certIndex) in the chain at index \(chainIndex) is not a valid base64 string.")
82
+ }
83
+ return data
79
84
  }
80
- return dataArray.isEmpty ? nil : dataArray
81
85
  }
82
86
  }
83
87
 
@@ -97,8 +101,8 @@ class IoReactNativeIso18013: RCTEventEmitter {
97
101
  do{
98
102
  let qrCodeString = try Proximity.shared.getQrCode()
99
103
  resolve(qrCodeString)
100
- } catch let error {
101
- ME.qrCodeError.reject(reject: reject, ("error", error.localizedDescription))
104
+ } catch {
105
+ reject(ModuleErrorCodes.getQrCodeError.rawValue, error.localizedDescription, error)
102
106
  }
103
107
  }
104
108
 
@@ -113,26 +117,31 @@ class IoReactNativeIso18013: RCTEventEmitter {
113
117
  - alias which is the alias of the key used to sign the credential;
114
118
  - docType which is the document type.
115
119
 
116
- - Throws: `ModuleException.invalidDocRequested.error()` and `ModuleException.invalidDocRequested.error()` iif an error occurs while parsing the document
120
+ - Throws: `ParsingError`:
121
+ - If the provided documents array do not contain a dictionary;
122
+ - If the provided dictionary doesn't adhere to the structure we expect;
123
+ - If the issuerSignedContent is not a valid base64 or base64url encoded string;
124
+ - If the creation of a ProximityDdocument fails.
117
125
 
118
126
  - Returns: An array of `ProximityDocument` containg the documents to be presented
119
127
  */
120
128
  private func parseDocuments(documents: [Any]) throws -> [ProximityDocument] {
121
- return try documents.compactMap{ (element) -> ProximityDocument in
122
- guard let dict : [String: Any] = element as? [String: Any] else { throw ModuleException.unableToDecode.error() }
129
+ return try documents.map{ (element) -> ProximityDocument in
130
+ 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
131
  guard
124
132
  let issuerSignedContent = dict["issuerSignedContent"] as? String,
125
133
  let alias = dict["alias"] as? String,
126
134
  let docType = dict["docType"] as? String,
127
135
  let decodedIssuerSignedContent = try? Base64Utils.decodeBase64OrBase64URL(base: issuerSignedContent)
128
- else { throw ModuleException.invalidDocRequested.error() }
129
-
136
+ else {
137
+ throw ParsingError.documentsNotValid ("The provided issuerSignedContent is not a valid base64 or base64url or don't contain the required fields alas, doctype and issuerSignedContent")
138
+ }
130
139
  guard let document = ProximityDocument(
131
140
  docType: docType,
132
141
  issuerSigned: [UInt8](decodedIssuerSignedContent),
133
142
  deviceKeyTag: alias
134
143
  ) else {
135
- throw ModuleException.invalidDocRequested.error()
144
+ throw ParsingError.documentsNotValid("The parsing for the document with docType \(docType) failed")
136
145
  }
137
146
  return document
138
147
  }
@@ -146,26 +155,26 @@ class IoReactNativeIso18013: RCTEventEmitter {
146
155
  - Parameters:
147
156
  - 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
157
 
149
- - Throws: `NSError` if a value doesn't has the ``AcceptedFieldsDict`` shape or the result dictionary is empty
158
+ - Throws: `ParsingError` if a value doesn't has the ``AcceptedFieldsDict`` shape or the result dictionary is empty
150
159
 
151
160
  - Returns: An ``AcceptedFieldsDict`` containg the accepted fields to be presented
152
161
 
153
162
  */
154
- private func parseAcceptedFields(acceptedFields: [AnyHashable: Any]) throws -> AcceptedFieldsDict {
163
+ private func parseAcceptedFields(acceptedFields: [AnyHashable: Any]) throws(ParsingError) -> AcceptedFieldsDict {
155
164
  var result: AcceptedFieldsDict = [:]
156
165
 
157
166
  for (key, value) in acceptedFields {
158
167
  guard let keyString = key as? String else {
159
- throw NSError(domain: "ParseAcceptedFields", code: -1, userInfo: [NSLocalizedDescriptionKey: "Key must be a String."])
168
+ throw ParsingError.acceptedFieldsNotValid("The accepted fields keys must be a String")
160
169
  }
161
170
  guard let valueDict = value as? [String: [String: Bool]] else {
162
- throw NSError(domain: "ParseAcceptedFields", code: -1, userInfo: [NSLocalizedDescriptionKey: "Value must be of type [String: [String: Bool]]."])
171
+ throw ParsingError.acceptedFieldsNotValid("The accepted fields values must be of type [String: [String: Bool]]")
163
172
  }
164
173
  result[keyString] = valueDict
165
174
  }
166
175
 
167
176
  if(result.isEmpty){
168
- throw NSError(domain: "ParseAcceptedFields", code: -1, userInfo: [NSLocalizedDescriptionKey: "Error parsing documents"])
177
+ throw ParsingError.acceptedFieldsNotValid("Empty accepted fields cannot be empty")
169
178
  }
170
179
 
171
180
  return result
@@ -216,8 +225,14 @@ class IoReactNativeIso18013: RCTEventEmitter {
216
225
  let deviceResponse = try Proximity.shared.generateDeviceResponse(items: items, documents: parsedDocuments, sessionTranscript: nil)
217
226
  let strDeviceResponse = Data(deviceResponse).base64EncodedString()
218
227
  resolve(strDeviceResponse)
219
- }catch{
220
- ME.generateDeviceResponseError.reject(reject: reject, ("error", error.localizedDescription))
228
+ }
229
+ catch let proximityError as ProximityError {
230
+ reject(ModuleErrorCodes.generateResponseError.rawValue, proximityError.description, proximityError)
231
+ }
232
+ catch let parsingError as ParsingError {
233
+ reject(ModuleErrorCodes.generateResponseError.rawValue, parsingError.description, parsingError)
234
+ } catch {
235
+ reject(ModuleErrorCodes.generateResponseError.rawValue, error.localizedDescription, error)
221
236
  }
222
237
  }
223
238
 
@@ -244,7 +259,7 @@ class IoReactNativeIso18013: RCTEventEmitter {
244
259
  resolve(true)
245
260
  }
246
261
  }catch let error {
247
- ME.sendResponseError.reject(reject: reject, ("error", error.localizedDescription))
262
+ reject(ModuleErrorCodes.sendResponseError.rawValue, error.localizedDescription, error)
248
263
  }
249
264
  }
250
265
 
@@ -262,16 +277,16 @@ class IoReactNativeIso18013: RCTEventEmitter {
262
277
  */
263
278
  @objc(sendErrorResponse:withResolver:withRejecter:)
264
279
  func sendErrorResponse(status: UInt64, _ resolve: @escaping RCTPromiseResolveBlock,
265
- reject: @escaping RCTPromiseRejectBlock){
280
+ reject: @escaping RCTPromiseRejectBlock){
266
281
  do{
267
282
  if let statusEnum = SessionDataStatus(rawValue: status) {
268
283
  try Proximity.shared.errorPresentation(statusEnum)
269
284
  } else {
270
- ME.sendResponseError.reject(reject: reject, ("error", "Invalid status code"))
285
+ reject(ModuleErrorCodes.sendErrorResponseError.rawValue, "Invalid status code provided: \(status)", nil)
271
286
  }
272
287
  resolve(true)
273
288
  }catch let error{
274
- ME.sendResponseError.reject(reject: reject, ("error", error.localizedDescription))
289
+ reject(ModuleErrorCodes.sendErrorResponseError.rawValue, error.localizedDescription, error)
275
290
  }
276
291
  }
277
292
 
@@ -392,102 +407,58 @@ class IoReactNativeIso18013: RCTEventEmitter {
392
407
 
393
408
  do {
394
409
  let sessionTranscript = Proximity.shared.generateOID4VPSessionTranscriptCBOR(
395
- clientId: clientId,
396
- responseUri: responseUri,
397
- authorizationRequestNonce: authorizationRequestNonce,
398
- mdocGeneratedNonce: mdocGeneratedNonce
410
+ clientId: clientId,
411
+ responseUri: responseUri,
412
+ authorizationRequestNonce: authorizationRequestNonce,
413
+ mdocGeneratedNonce: mdocGeneratedNonce
399
414
  )
400
-
401
- let documentsAsProximityDocument = try parseDocuments(documents: documents)
402
415
 
416
+ let documentsAsProximityDocument = try parseDocuments(documents: documents)
403
417
  let items = try JSONDecoder().decode([String : [String : [String : Bool]]].self, from: Data(fieldRequestedAndAccepted.utf8))
404
-
405
- do {
406
- let response = try Proximity.shared.generateDeviceResponse(items: items, documents: documentsAsProximityDocument, sessionTranscript: sessionTranscript)
407
- resolve(Data(response).base64EncodedString())
408
- } catch {
409
- ME.unableToGenerateResponse.reject(reject: reject)
410
- return
411
- }
412
- } catch let error as NSError where error.domain == ME.invalidDocRequested.rawValue {
413
- ME.invalidDocRequested.reject(reject: reject)
414
- } catch _ as DecodingError {
415
- ME.invalidItems.reject(reject: reject)
416
- } catch {
417
- ME.unexpected.reject(reject: reject)
418
+ let response = try Proximity.shared.generateDeviceResponse(items: items, documents: documentsAsProximityDocument, sessionTranscript: sessionTranscript)
419
+ resolve(Data(response).base64EncodedString())
420
+ } catch let parsingError as ParsingError{
421
+ reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, parsingError.description, parsingError)
422
+ } catch let proximityError as ProximityError{
423
+ reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, proximityError.description, proximityError)
418
424
  }
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
425
+ catch {
426
+ reject(ModuleErrorCodes.generateOID4VPResponseError.rawValue, error.localizedDescription, error)
430
427
  }
431
428
  }
432
-
429
+
433
430
  /**
434
- Wrapper for rejecting with an error.
435
- Add a new case in order to extend the possible errors.
431
+ Custom Error which is thrown when a parsing error occurs in our utility functions which converts data from the bridge to what
432
+ our underlying functions expect.
433
+ This is needed in order to provide a customized description which can include more debug information.
436
434
  */
437
- private enum ModuleException: String, CaseIterable {
435
+ enum ParsingError : Error, CustomStringConvertible {
436
+ case documentsNotValid(String)
437
+ case certificatesNotValid(String)
438
+ case acceptedFieldsNotValid(String)
439
+
440
+ public var description: String {
441
+ switch(self) {
442
+ case .documentsNotValid(let message):
443
+ return message
444
+ case .certificatesNotValid(let message):
445
+ return message
446
+ case .acceptedFieldsNotValid(let message):
447
+ return message
448
+ }
449
+ }
450
+ }
451
+
452
+ // Errors which this module uses to reject a promise
453
+ private enum ModuleErrorCodes: String, CaseIterable {
454
+ // ISO18013-5 related errors
438
455
  case startError = "START_ERROR"
439
- case qrCodeError = "QR_CODE_ERROR"
440
- case generateDeviceResponseError = "GENERATE_DEVICE_RESPONSE_ERROR"
456
+ case getQrCodeError = "GET_QR_CODE_ERROR"
441
457
  case sendResponseError = "SEND_RESPONSE_ERROR"
442
- case sendErrorResponse = "SEND_ERROR_RESPONSE_ERROR"
443
- case invalidDocRequested = "DOC_REQUESTED_PARSING_ERROR"
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"
448
-
449
- func error(
450
- userInfo: [String : Any]? = nil
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
- }
458
+ case sendErrorResponseError = "SEND_ERROR_RESPONSE_ERROR"
459
+ case generateResponseError = "GENERATE_RESPONSE_ERROR"
475
460
 
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
- }
461
+ // ISO18013-7 related errors
462
+ case generateOID4VPResponseError = "GENERATE_OID4VP_RESPONSE_ERROR"
492
463
  }
493
464
  }
@@ -88,8 +88,40 @@ enum DocumentTypeEnum {
88
88
 
89
89
  ### Error Codes
90
90
 
91
- | Type | Platform | Description |
92
- | ----------------- | ----------- | --------------------------------------------- |
93
- | INVALID_ENCODING | Android/iOS | Provided payload has incorrect encoding |
94
- | UNABLE_TO_DECODE | Android/iOS | The data does not contain a valid CBOR object |
95
- | UNKNOWN_EXCEPTION | Android/iOS | Unexpected failure |
91
+ This table contains the list of error codes that can be thrown by the `CBOR` module which are mapped via the `ModuleErrorCodes` type:
92
+ | Type | Platform | Description |
93
+ | -------------------------- | ----------- | ------------------------------------------------------------ |
94
+ | DECODE_ERROR | Android/iOS | An error occurred while decoding the CBOR |
95
+ | DECODE_DOCUMENTS_ERROR | Android/iOS | An error occurred while decoding a CBOR mDOC |
96
+ | DECODE_ISSUER_SIGNED_ERROR | Android/iOS | An error occurred while decoding a CBOR Issuer Signed object |
97
+
98
+ An error can be parsed using the `ModuleErrorSchema` with type `ModuleErrorCodes` exposed by the `ISO18013_5` module. The error can be parsed as follows:
99
+
100
+ ```typescript
101
+ import { CBOR } from '@pagopa/io-react-native-iso18013';
102
+ try {
103
+ await CBOR.func();
104
+ } catch (error) {
105
+ const parsedError = CBOR.ModuleErrorSchema.parse(error); // Or ModuleErrorSchema.safeParse(error) for safe parsing
106
+ console.log(JSON.stringify(parsedError, null, 2));
107
+ }
108
+ ```
109
+
110
+ The parsed object will contain properties from both iOS and Android platforms:
111
+
112
+ ```typescript
113
+ {
114
+ code: string; // Defined in ModuleErrorCodes
115
+ message: string;
116
+ name: string;
117
+ userInfo?: Record<string, any> | null;
118
+ nativeStackAndroid?: Array<{
119
+ lineNumber: number;
120
+ file: string;
121
+ methodName: string;
122
+ class: string;
123
+ }>;
124
+ domain?: string;
125
+ nativeStackIOS?: Array<string>;
126
+ };
127
+ ```
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ import z from 'zod';
4
+ import { GenericModuleErrorSchema } from "../../schema.js";
5
+
6
+ /**
7
+ * Error codes which the CBOR module uses to reject a promise.
8
+ */
9
+ const ModuleErrorCodesSchema = z.enum(['DECODE_ERROR', 'DECODE_DOCUMENTS_ERROR', 'DECODE_ISSUER_SIGNED_ERROR', 'EUNSPECIFIED' // Android only default when no other error is specified
10
+ ]);
11
+ /**
12
+ * Schema which can be used to parse a rejected promise error by the CBOR module.
13
+ */
14
+ export const ModuleErrorSchema = GenericModuleErrorSchema(ModuleErrorCodesSchema);
15
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["z","GenericModuleErrorSchema","ModuleErrorCodesSchema","enum","ModuleErrorSchema"],"sourceRoot":"../../../../src","sources":["cbor/cbor/error.ts"],"mappings":";;AAAA,OAAOA,CAAC,MAAM,KAAK;AACnB,SAASC,wBAAwB,QAAQ,iBAAc;;AAEvD;AACA;AACA;AACA,MAAMC,sBAAsB,GAAGF,CAAC,CAACG,IAAI,CAAC,CACpC,cAAc,EACd,wBAAwB,EACxB,4BAA4B,EAC5B,cAAc,CAAE;AAAA,CACjB,CAAC;AAIF;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,GAAGH,wBAAwB,CACvDC,sBACF,CAAC","ignoreList":[]}
@@ -2,4 +2,5 @@
2
2
 
3
3
  export { decode, decodeDocuments, decodeIssuerSigned } from "./decoder.js";
4
4
  export { DocumentValue, Documents, DocumentsFromString, IssuerSigned, MDOC } from "./schema.js";
5
+ export { ModuleErrorSchema } from "./error.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["decode","decodeDocuments","decodeIssuerSigned","DocumentValue","Documents","DocumentsFromString","IssuerSigned","MDOC"],"sourceRoot":"../../../../src","sources":["cbor/cbor/index.ts"],"mappings":";;AAAA,SAASA,MAAM,EAAEC,eAAe,EAAEC,kBAAkB,QAAQ,cAAW;AACvE,SACEC,aAAa,EACbC,SAAS,EACTC,mBAAmB,EACnBC,YAAY,EACZC,IAAI,QACC,aAAU","ignoreList":[]}
1
+ {"version":3,"names":["decode","decodeDocuments","decodeIssuerSigned","DocumentValue","Documents","DocumentsFromString","IssuerSigned","MDOC","ModuleErrorSchema"],"sourceRoot":"../../../../src","sources":["cbor/cbor/index.ts"],"mappings":";;AAAA,SAASA,MAAM,EAAEC,eAAe,EAAEC,kBAAkB,QAAQ,cAAW;AACvE,SACEC,aAAa,EACbC,SAAS,EACTC,mBAAmB,EACnBC,YAAY,EACZC,IAAI,QACC,aAAU;AACjB,SAGEC,iBAAiB,QACZ,YAAS","ignoreList":[]}