react-native-security-suite 0.9.22 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +235 -69
  2. package/android/build.gradle +11 -0
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/securitysuite/CryptoConfig.java +158 -0
  5. package/android/src/main/java/com/securitysuite/CryptoUtils.java +152 -0
  6. package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
  7. package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
  8. package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
  9. package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
  10. package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
  11. package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
  12. package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
  13. package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
  14. package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +310 -102
  15. package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
  16. package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
  17. package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
  18. package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
  19. package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
  20. package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
  21. package/ios/CryptoConfig.swift +124 -0
  22. package/ios/JWSGenerator.swift +288 -0
  23. package/ios/JWSGeneratorTests.swift +168 -0
  24. package/ios/KeychainHelper.swift +104 -0
  25. package/ios/Obfuscation.swift +42 -0
  26. package/ios/SecureStorageNative.swift +84 -0
  27. package/ios/Security/AppIntegrityChecker.swift +85 -0
  28. package/ios/Security/EmulatorDetector.swift +45 -0
  29. package/ios/Security/RuntimeDetector.swift +107 -0
  30. package/ios/SecuritySuite.mm +28 -4
  31. package/ios/SecuritySuite.swift +407 -131
  32. package/ios/SslPinning.swift +242 -263
  33. package/lib/commonjs/clipboard/index.js +3 -0
  34. package/lib/commonjs/clipboard/index.js.map +1 -0
  35. package/lib/commonjs/crypto/index.js +39 -0
  36. package/lib/commonjs/crypto/index.js.map +1 -0
  37. package/lib/commonjs/device/index.js +40 -0
  38. package/lib/commonjs/device/index.js.map +1 -0
  39. package/lib/commonjs/errors.js +62 -0
  40. package/lib/commonjs/errors.js.map +1 -0
  41. package/lib/commonjs/index.js +220 -151
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/integrity/index.js +40 -0
  44. package/lib/commonjs/integrity/index.js.map +1 -0
  45. package/lib/commonjs/jws.js +141 -0
  46. package/lib/commonjs/jws.js.map +1 -0
  47. package/lib/commonjs/legacy/cryptoOptions.js +20 -0
  48. package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
  49. package/lib/commonjs/native/bridge.js +23 -0
  50. package/lib/commonjs/native/bridge.js.map +1 -0
  51. package/lib/commonjs/network/index.js +3 -0
  52. package/lib/commonjs/network/index.js.map +1 -0
  53. package/lib/commonjs/risk/score.js +36 -0
  54. package/lib/commonjs/risk/score.js.map +1 -0
  55. package/lib/commonjs/runtime/index.js +31 -0
  56. package/lib/commonjs/runtime/index.js.map +1 -0
  57. package/lib/commonjs/screen/index.js +13 -0
  58. package/lib/commonjs/screen/index.js.map +1 -0
  59. package/lib/commonjs/securitySuite/index.js +42 -0
  60. package/lib/commonjs/securitySuite/index.js.map +1 -0
  61. package/lib/commonjs/storage/index.js +3 -0
  62. package/lib/commonjs/storage/index.js.map +1 -0
  63. package/lib/commonjs/types/detection.js +2 -0
  64. package/lib/commonjs/types/detection.js.map +1 -0
  65. package/lib/module/clipboard/index.js +3 -0
  66. package/lib/module/clipboard/index.js.map +1 -0
  67. package/lib/module/crypto/index.js +35 -0
  68. package/lib/module/crypto/index.js.map +1 -0
  69. package/lib/module/device/index.js +36 -0
  70. package/lib/module/device/index.js.map +1 -0
  71. package/lib/module/errors.js +55 -0
  72. package/lib/module/errors.js.map +1 -0
  73. package/lib/module/index.js +147 -148
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/integrity/index.js +36 -0
  76. package/lib/module/integrity/index.js.map +1 -0
  77. package/lib/module/jws.js +127 -0
  78. package/lib/module/jws.js.map +1 -0
  79. package/lib/module/legacy/cryptoOptions.js +16 -0
  80. package/lib/module/legacy/cryptoOptions.js.map +1 -0
  81. package/lib/module/native/bridge.js +19 -0
  82. package/lib/module/native/bridge.js.map +1 -0
  83. package/lib/module/network/index.js +3 -0
  84. package/lib/module/network/index.js.map +1 -0
  85. package/lib/module/risk/score.js +32 -0
  86. package/lib/module/risk/score.js.map +1 -0
  87. package/lib/module/runtime/index.js +27 -0
  88. package/lib/module/runtime/index.js.map +1 -0
  89. package/lib/module/screen/index.js +5 -0
  90. package/lib/module/screen/index.js.map +1 -0
  91. package/lib/module/securitySuite/index.js +38 -0
  92. package/lib/module/securitySuite/index.js.map +1 -0
  93. package/lib/module/storage/index.js +3 -0
  94. package/lib/module/storage/index.js.map +1 -0
  95. package/lib/module/types/detection.js +2 -0
  96. package/lib/module/types/detection.js.map +1 -0
  97. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
  98. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
  100. package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
  102. package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
  104. package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
  106. package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/src/errors.d.ts +17 -0
  108. package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/src/index.d.ts +77 -24
  111. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
  113. package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/src/jws.d.ts +44 -0
  115. package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
  116. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
  117. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
  119. package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
  121. package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
  123. package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
  125. package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
  126. package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
  127. package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
  129. package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
  131. package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
  133. package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
  134. package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
  135. package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
  136. package/lib/typescript/module/src/SecureView.d.ts +1 -1
  137. package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
  138. package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
  139. package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
  140. package/lib/typescript/module/src/crypto/index.d.ts +15 -0
  141. package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
  142. package/lib/typescript/module/src/device/index.d.ts +11 -0
  143. package/lib/typescript/module/src/device/index.d.ts.map +1 -0
  144. package/lib/typescript/module/src/errors.d.ts +17 -0
  145. package/lib/typescript/module/src/errors.d.ts.map +1 -0
  146. package/lib/typescript/module/src/helpers.d.ts.map +1 -1
  147. package/lib/typescript/module/src/index.d.ts +77 -24
  148. package/lib/typescript/module/src/index.d.ts.map +1 -1
  149. package/lib/typescript/module/src/integrity/index.d.ts +6 -0
  150. package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
  151. package/lib/typescript/module/src/jws.d.ts +44 -0
  152. package/lib/typescript/module/src/jws.d.ts.map +1 -0
  153. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
  154. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
  155. package/lib/typescript/module/src/native/bridge.d.ts +12 -0
  156. package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
  157. package/lib/typescript/module/src/network/index.d.ts +2 -0
  158. package/lib/typescript/module/src/network/index.d.ts.map +1 -0
  159. package/lib/typescript/module/src/risk/score.d.ts +12 -0
  160. package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
  161. package/lib/typescript/module/src/runtime/index.d.ts +6 -0
  162. package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
  163. package/lib/typescript/module/src/screen/index.d.ts +3 -0
  164. package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
  165. package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
  166. package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
  167. package/lib/typescript/module/src/storage/index.d.ts +2 -0
  168. package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
  169. package/lib/typescript/module/src/types/detection.d.ts +41 -0
  170. package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
  171. package/package.json +2 -10
  172. package/src/clipboard/index.ts +1 -0
  173. package/src/crypto/index.ts +49 -0
  174. package/src/device/index.ts +47 -0
  175. package/src/errors.ts +84 -0
  176. package/src/index.tsx +293 -195
  177. package/src/integrity/index.ts +46 -0
  178. package/src/jws.ts +213 -0
  179. package/src/legacy/cryptoOptions.ts +49 -0
  180. package/src/native/bridge.ts +37 -0
  181. package/src/network/index.ts +1 -0
  182. package/src/risk/score.ts +49 -0
  183. package/src/runtime/index.ts +43 -0
  184. package/src/screen/index.ts +2 -0
  185. package/src/securitySuite/index.ts +45 -0
  186. package/src/storage/index.ts +1 -0
  187. package/src/types/detection.ts +46 -0
  188. package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
  189. package/ios/StorageEncryption.swift +0 -89
@@ -1,303 +1,282 @@
1
1
  import Pulse
2
+ import Foundation
3
+ import Security
4
+ import CommonCrypto
2
5
 
3
6
  @available(iOS 13.0, *)
4
- class SSLPinning: NSObject, URLSessionDataDelegate {
5
- var url: NSString!
6
- var data: NSDictionary!
7
- var callback: RCTResponseSenderBlock!
8
- var validDomains: [String] = []
9
- var intermediateKeyHashes: [Data] = []
10
- var leafKeyHashes: [Data] = []
11
- var networkLogger: NetworkLogger = .init()
12
- var responseData: Data = Data()
13
- let pulseNotification = PulseUINotification()
14
-
15
- init(url: NSString, data: NSDictionary, callback: @escaping RCTResponseSenderBlock) {
16
- self.url = url
17
- self.data = data
18
- self.callback = callback
19
- }
20
-
21
- func urlSession(
22
- _ session: URLSession,
23
- didReceive challenge: URLAuthenticationChallenge,
24
- completionHandler: @escaping (
25
- URLSession.AuthChallengeDisposition,
26
- URLCredential?
27
- ) -> Void
28
- ) {
29
- if let certs = data["certificates"] as? [String] {
30
- for cert in certs {
31
- let filteredCert = cert.replacingOccurrences(
32
- of: "sha256/",
33
- with: "",
34
- options: .caseInsensitive,
35
- range: nil
36
- )
37
- intermediateKeyHashes.append(Data(base64Encoded: filteredCert)!)
38
- }
39
- } else {
40
- return completionHandler(.performDefaultHandling, nil)
41
- }
42
-
43
- if let domains = data["validDomains"] as? [String] {
44
- for domain in domains {
45
- validDomains.append(domain)
46
- }
47
- } else {
48
- return completionHandler(.performDefaultHandling, nil)
49
- }
50
-
51
- guard let trust = challenge.protectionSpace.serverTrust else {
52
- return completionHandler(.cancelAuthenticationChallenge, nil)
53
- }
7
+ final class PinningConfiguration {
8
+ let enabled: Bool
9
+ let error: String?
10
+ let validDomains: [String]
11
+ let pinHashes: Set<Data>
12
+
13
+ init(data: NSDictionary) {
14
+ let hasCertificates = data["certificates"] != nil
15
+ let hasValidDomains = data["validDomains"] != nil
16
+
17
+ if hasCertificates != hasValidDomains {
18
+ self.enabled = false
19
+ self.error = "SSL pinning requires both 'certificates' (SPKI SHA-256 hashes) and 'validDomains'"
20
+ self.validDomains = []
21
+ self.pinHashes = []
22
+ return
23
+ }
24
+
25
+ guard hasCertificates, let certs = data["certificates"] as? [String],
26
+ let domains = data["validDomains"] as? [String] else {
27
+ self.enabled = false
28
+ self.error = nil
29
+ self.validDomains = []
30
+ self.pinHashes = []
31
+ return
32
+ }
33
+
34
+ guard !certs.isEmpty else {
35
+ self.enabled = false
36
+ self.error = "At least one certificate/public key pin is required"
37
+ self.validDomains = []
38
+ self.pinHashes = []
39
+ return
40
+ }
41
+
42
+ guard !domains.isEmpty else {
43
+ self.enabled = false
44
+ self.error = "At least one valid domain is required"
45
+ self.validDomains = []
46
+ self.pinHashes = []
47
+ return
48
+ }
49
+
50
+ self.validDomains = domains
51
+ .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() }
52
+ .filter { !$0.isEmpty }
53
+ self.pinHashes = Set(certs.compactMap { cert in
54
+ let filtered = cert
55
+ .replacingOccurrences(of: "sha256/", with: "", options: .caseInsensitive)
56
+ .trimmingCharacters(in: .whitespacesAndNewlines)
57
+ return Data(base64Encoded: filtered)
58
+ }.filter { !$0.isEmpty })
54
59
 
55
- let host = challenge.protectionSpace.host
56
- let port = challenge.protectionSpace.port
57
- guard port == 443, (3...4).contains(trust.certificates.count),
58
- let leafCertificate = trust.certificates.first,
59
- let commonName = leafCertificate.commonName,
60
- validDomains
61
- .contains(where: { commonName == $0 || commonName.hasSuffix("." + $0) }) else {
62
- completionHandler(.cancelAuthenticationChallenge, nil)
63
- return
60
+ if self.pinHashes.isEmpty {
61
+ self.enabled = false
62
+ self.error = "No valid SPKI SHA-256 pins found in certificates"
63
+ } else {
64
+ self.enabled = true
65
+ self.error = nil
66
+ }
64
67
  }
65
- let intermediateCertificatesValid = trust.certificates.dropFirst().prefix(2).allSatisfy {
66
- ($0.pin.map(intermediateKeyHashes.contains) ?? false)
68
+ }
69
+
70
+ @available(iOS 13.0, *)
71
+ class SSLPinning: NSObject, URLSessionDataDelegate {
72
+ var url: NSString!
73
+ var data: NSDictionary!
74
+ var callback: RCTResponseSenderBlock!
75
+ let config: PinningConfiguration
76
+ var loggerEnabled = false
77
+ var networkLogger: NetworkLogger = .init()
78
+ var responseData: Data = Data()
79
+ let pulseNotification = PulseUINotification()
80
+
81
+ init(url: NSString, data: NSDictionary, callback: @escaping RCTResponseSenderBlock) {
82
+ self.url = url
83
+ self.data = data
84
+ self.callback = callback
85
+ self.config = PinningConfiguration(data: data)
86
+ super.init()
87
+
88
+ #if DEBUG
89
+ loggerEnabled = (data["loggerIsEnabled"] as? Bool) == true
90
+ #else
91
+ loggerEnabled = false
92
+ #endif
67
93
  }
68
- let leafCertificateValid = leafKeyHashes.contains(
69
- leafCertificate.pin ?? .init()
70
- )
71
-
72
- let pattern = commonName
73
- .replacingOccurrences(of: ".", with: "\\.")
74
- .replacingOccurrences(of: "*", with: ".+", options: [.anchored])
75
- guard intermediateCertificatesValid && (
76
- leafKeyHashes.isEmpty || leafCertificateValid
77
- ),
78
- let commonNameRegex = try? NSRegularExpression(pattern: pattern) else {
79
- completionHandler(.cancelAuthenticationChallenge, nil)
80
- return
94
+
95
+ static func isHttpsURL(_ urlString: String) -> Bool {
96
+ guard let url = URL(string: urlString.trimmingCharacters(in: .whitespacesAndNewlines)),
97
+ let scheme = url.scheme?.lowercased(),
98
+ let host = url.host, !host.isEmpty else {
99
+ return false
100
+ }
101
+ return scheme == "https"
81
102
  }
82
-
83
- guard commonNameRegex.textMatches(in: host) == [host] else {
84
- completionHandler(.cancelAuthenticationChallenge, nil)
85
- return
103
+
104
+ static func hostnameMatchesValidDomains(_ host: String, validDomains: [String]) -> Bool {
105
+ let normalizedHost = host.lowercased()
106
+ return validDomains.contains { domain in
107
+ normalizedHost == domain || normalizedHost.hasSuffix("." + domain)
108
+ }
86
109
  }
87
-
88
- trust.policies = [.ssl(server: true, hostname: host)]
89
- do {
90
- if try !trust.evaluate() {
91
- completionHandler(.cancelAuthenticationChallenge, nil)
92
- }
93
- } catch {
94
- completionHandler(.cancelAuthenticationChallenge, nil)
95
- return
110
+
111
+ func urlSession(
112
+ _ session: URLSession,
113
+ didReceive challenge: URLAuthenticationChallenge,
114
+ completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
115
+ ) {
116
+ guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else {
117
+ completionHandler(.performDefaultHandling, nil)
118
+ return
119
+ }
120
+
121
+ guard config.enabled else {
122
+ // No pinning configured: standard TLS validation only.
123
+ completionHandler(.performDefaultHandling, nil)
124
+ return
125
+ }
126
+
127
+ guard let trust = challenge.protectionSpace.serverTrust else {
128
+ completionHandler(.cancelAuthenticationChallenge, nil)
129
+ return
130
+ }
131
+
132
+ let host = challenge.protectionSpace.host.lowercased()
133
+ let port = challenge.protectionSpace.port
134
+
135
+ guard port == 443 else {
136
+ completionHandler(.cancelAuthenticationChallenge, nil)
137
+ return
138
+ }
139
+
140
+ guard Self.hostnameMatchesValidDomains(host, validDomains: config.validDomains) else {
141
+ completionHandler(.cancelAuthenticationChallenge, nil)
142
+ return
143
+ }
144
+
145
+ // Enforce hostname policy before trust evaluation.
146
+ SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, host as CFString))
147
+
148
+ var pinMatched = false
149
+ let certificateCount = SecTrustGetCertificateCount(trust)
150
+ for index in 0..<certificateCount {
151
+ guard let certificate = SecTrustGetCertificateAtIndex(trust, index) else { continue }
152
+
153
+ if let spkiHash = certificate.publicKeyPinHash, config.pinHashes.contains(spkiHash) {
154
+ pinMatched = true
155
+ break
156
+ }
157
+
158
+ let certData = SecCertificateCopyData(certificate) as Data
159
+ let certHash = certData._hash()
160
+ if config.pinHashes.contains(certHash) {
161
+ pinMatched = true
162
+ break
163
+ }
164
+ }
165
+
166
+ guard pinMatched else {
167
+ completionHandler(.cancelAuthenticationChallenge, nil)
168
+ return
169
+ }
170
+
171
+ var error: CFError?
172
+ guard SecTrustEvaluateWithError(trust, &error) else {
173
+ completionHandler(.cancelAuthenticationChallenge, nil)
174
+ return
175
+ }
176
+
177
+ completionHandler(.useCredential, URLCredential(trust: trust))
96
178
  }
97
-
98
- let credential = URLCredential(trust: trust)
99
- completionHandler(.useCredential, credential)
100
- }
101
-
179
+
102
180
  func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) {
103
- networkLogger.logTaskCreated(task)
181
+ guard loggerEnabled else { return }
182
+ networkLogger.logTaskCreated(task)
104
183
  }
105
-
184
+
106
185
  func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
107
- networkLogger.logDataTask(dataTask, didReceive: data)
108
- responseData.append(data)
186
+ if loggerEnabled {
187
+ networkLogger.logDataTask(dataTask, didReceive: data)
188
+ }
189
+ responseData.append(data)
109
190
  }
110
-
191
+
111
192
  func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
112
- networkLogger.logTask(task, didCompleteWithError: error)
113
-
114
- if let error = error {
115
- callback([nil, error.localizedDescription])
116
- } else {
117
- if let httpResponse = task.response as? HTTPURLResponse {
118
- let url = httpResponse.url?.absoluteString ?? url! as String
119
- let status = httpResponse.statusCode
120
- let headers = httpResponse.allHeaderFields
121
-
122
- if(httpResponse.statusCode > 299) {
123
- callback([NSNull(), [
124
- "url": url,
125
- "status": status,
126
- "headers": status,
127
- "error": String(data: responseData, encoding: .utf8) ?? "",
193
+ if loggerEnabled {
194
+ networkLogger.logTask(task, didCompleteWithError: error)
195
+ }
196
+
197
+ if let error = error {
198
+ callback([NSNull(), error.localizedDescription])
199
+ return
200
+ }
201
+
202
+ guard let httpResponse = task.response as? HTTPURLResponse else {
203
+ callback([NSNull(), "Unknown error occurred"])
204
+ return
205
+ }
206
+
207
+ let responseUrl = httpResponse.url?.absoluteString ?? (url as String)
208
+ let status = httpResponse.statusCode
209
+ let headers = Self.sanitizedHeaders(httpResponse.allHeaderFields)
210
+
211
+ if status > 299 {
212
+ callback([NSNull(), [
213
+ "url": responseUrl,
214
+ "status": status,
215
+ "headers": headers,
216
+ "error": String(data: responseData, encoding: .utf8) ?? "",
128
217
  ]])
129
- } else {
218
+ } else {
130
219
  callback([[
131
- "url": url,
132
- "status": status,
133
- "headers": headers,
134
- "response": String.init(decoding: responseData, as: UTF8.self),
135
- "responseJson": try? JSONSerialization.jsonObject(with: responseData, options: []),
220
+ "url": responseUrl,
221
+ "status": status,
222
+ "headers": headers,
223
+ "response": String(decoding: responseData, as: UTF8.self),
224
+ "responseJson": try? JSONSerialization.jsonObject(with: responseData, options: []),
136
225
  ], NSNull()])
137
- }
138
- } else {
139
- callback([NSNull(), "Unknown error occurred"])
140
226
  }
141
- }
142
227
  }
143
228
 
144
229
  func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
230
+ guard loggerEnabled else { return }
145
231
  networkLogger.logTask(task, didFinishCollecting: metrics)
146
- if (data["loggerIsEnabled"] as? Bool) == true {
147
- if let httpResponse = task.response as? HTTPURLResponse {
148
- if let url = URL(string: httpResponse.url?.absoluteString ?? url! as String) {
149
- pulseNotification.showNotification(body: "\(httpResponse.statusCode) \(url.relativePath)")
232
+ if let httpResponse = task.response as? HTTPURLResponse,
233
+ let responseURL = URL(string: httpResponse.url?.absoluteString ?? (url as String)) {
234
+ pulseNotification.showNotification(body: "\(httpResponse.statusCode) \(responseURL.relativePath)")
235
+ }
236
+ }
237
+
238
+ private static let sensitiveHeaders: Set<String> = [
239
+ "authorization", "proxy-authorization", "cookie", "set-cookie",
240
+ "x-api-key", "x-auth-token", "x-access-token", "x-jws-signature", "x-request-signature", "x-csrf-token",
241
+ ]
242
+
243
+ static func sanitizedHeaders(_ headers: [AnyHashable: Any]) -> [String: String] {
244
+ var result: [String: String] = [:]
245
+ for (key, value) in headers {
246
+ guard let name = key as? String else { continue }
247
+ let stringValue = String(describing: value)
248
+ if sensitiveHeaders.contains(name.lowercased()) {
249
+ result[name] = maskValue(stringValue)
250
+ } else {
251
+ result[name] = stringValue
150
252
  }
151
- }
152
253
  }
254
+ return result
255
+ }
256
+
257
+ static func maskValue(_ value: String) -> String {
258
+ guard value.count > 8 else { return "***" }
259
+ return String(value.prefix(4)) + "***" + String(value.suffix(2))
153
260
  }
154
261
  }
155
262
 
156
263
  @available(iOS 13.0, *)
157
264
  extension Data {
158
- /**
159
- Calculates hash digest of data.
160
-
161
- - Parameter digest: digest type. Currently only SHA is supported.
162
- - Returns: A data object with length equal to digest length.
163
- */
164
265
  public func _hash() -> Data {
165
266
  guard !isEmpty else { return Data() }
166
- var result = [UInt8](repeating: 0, count: 256/8)
167
- self.withUnsafeBytes { (buf: UnsafeRawBufferPointer) -> Void in
168
- let ptr = buf.baseAddress!
169
- let dataLen = CC_LONG(buf.count)
170
- CC_SHA256(ptr, dataLen, &result)
267
+ var result = [UInt8](repeating: 0, count: 256 / 8)
268
+ self.withUnsafeBytes { (buf: UnsafeRawBufferPointer) in
269
+ guard let ptr = buf.baseAddress else { return }
270
+ _ = CC_SHA256(ptr, CC_LONG(buf.count), &result)
171
271
  }
172
-
173
272
  return Data(result)
174
273
  }
175
274
  }
176
275
 
177
- extension Dictionary {
178
- func toString() -> String? {
179
- return (self.compactMap({ (key, value) -> String in
180
- return "\(key)=\(value)"
181
- }) as Array).joined(separator: "&")
182
- }
183
- }
184
-
185
- @available(iOS 13.0, *)
186
- extension SecTrust {
187
- // Returns certificates of the certificate chain used to evaluate trust.
188
- fileprivate var certificates: [SecCertificate] {
189
- (0..<SecTrustGetCertificateCount(self))
190
- .map { SecTrustGetCertificateAtIndex(self, $0 as CFIndex)! }
191
- }
192
-
193
- // Retrieves the policies used by a given trust management object.
194
- fileprivate var policies: [SecPolicy]? {
195
- get {
196
- var result: CFArray?
197
- SecTrustCopyPolicies(self, &result)
198
- return result as? [SecPolicy]
199
- }
200
- set {
201
- if let newValue = newValue {
202
- SecTrustSetPolicies(self, newValue as CFArray)
203
- } else {
204
- SecTrustSetPolicies(self, [] as CFArray)
205
- }
206
- }
207
- }
208
-
209
- fileprivate func evaluate() throws -> Bool {
210
- var error: CFError?
211
- let success = SecTrustEvaluateWithError(self, &error)
212
- if let error = error {
213
- throw error
214
- }
215
- return success
216
- }
217
- }
218
-
219
276
  @available(iOS 13.0, *)
220
277
  extension SecCertificate {
221
- fileprivate var key: SecKey? {
222
- SecCertificateCopyKey(self)
223
- }
224
-
225
- fileprivate var commonName: String? {
226
- var result: CFString?
227
- SecCertificateCopyCommonName(self, &result)
228
- return result as String?
229
- }
230
-
231
- fileprivate var pin: Data? {
232
- try? key?.bytes().hash(digest: .sha256)
233
- }
234
- }
235
-
236
- extension SecPolicy {
237
- static func ssl(server: Bool, hostname: String) -> SecPolicy {
238
- SecPolicyCreateSSL(server, hostname as CFString)
239
- }
240
-
241
- static func basicX509() -> SecPolicy {
242
- SecPolicyCreateBasicX509()
243
- }
244
- }
245
-
246
- extension NSRegularExpression {
247
- public func matches(
248
- in string: String,
249
- options: NSRegularExpression.MatchingOptions = []
250
- ) -> [NSTextCheckingResult] {
251
- matches(in: string, options: options, range: NSRange(string.startIndex..., in: string))
252
- }
253
-
254
- public func textMatches(
255
- in string: String,
256
- options: NSRegularExpression.MatchingOptions = []
257
- ) -> [String] {
258
- textMatches(in: string, options: options, range: string.startIndex...)
259
- }
260
-
261
- public func matches<R: RangeExpression>(
262
- in string: String,
263
- options: NSRegularExpression.MatchingOptions = [],
264
- range: R
265
- ) -> [NSTextCheckingResult] where R.Bound == String.Index {
266
- matches(in: string, options: options, range: NSRange(range, in: string))
267
- }
268
-
269
- public func textMatches<R: RangeExpression>(
270
- in string: String,
271
- options: NSRegularExpression.MatchingOptions = [],
272
- range: R
273
- ) -> [String] where R.Bound == String.Index {
274
- matches(in: string, options: options, range: NSRange(range, in: string))
275
- .map {
276
- guard let range = Range($0.range, in: string) else {
277
- return ""
278
- }
279
- return String(string[range])
280
- }
281
- }
282
- }
283
-
284
- struct JoseHeader: Codable {
285
- internal init(alg: String = "HS256", kid: String, b64: Bool = false, crit: [String] = ["b64"], requestId: String) {
286
- self.alg = alg
287
- self.kid = kid
288
- self.b64 = b64
289
- self.crit = crit
290
- self.requestId = requestId
291
- }
292
-
293
- private enum CodingKeys: String, CodingKey {
294
- case alg, kid, b64, crit
295
- case requestId = "request_id"
278
+ fileprivate var publicKeyPinHash: Data? {
279
+ guard let key = SecCertificateCopyKey(self) else { return nil }
280
+ return try? key.bytes().hash(digest: .sha256)
296
281
  }
297
-
298
- let alg: String
299
- let kid: String
300
- let b64: Bool
301
- let crit: [String]
302
- let requestId: String
303
282
  }
@@ -0,0 +1,3 @@
1
+ /** Phase 2+ namespace placeholder. */
2
+ "use strict";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["clipboard/index.ts"],"mappings":"AAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Crypto = void 0;
7
+ var _bridge = require("../native/bridge.js");
8
+ function toNativeCryptoOptions(options) {
9
+ return {
10
+ keyAgreementAlgorithm: options?.keyAgreementAlgorithm ?? 'X25519',
11
+ keyFactoryAlgorithm: options?.keyType ?? options?.keyFactoryAlgorithm ?? 'OKP',
12
+ encryptionKeyAlgorithm: options?.encryptionKeyAlgorithm ?? 'AES-256',
13
+ hmacKeyAlgorithm: options?.hmacAlgorithm ?? options?.hmacKeyAlgorithm ?? 'HMAC-SHA-512',
14
+ cipherTransformation: options?.cipher ?? options?.cipherTransformation ?? 'AES-GCM',
15
+ gcmTagLength: options?.tagLength ?? options?.gcmTagLength ?? 128,
16
+ gcmIvLength: options?.ivLength ?? options?.gcmIvLength ?? 12
17
+ };
18
+ }
19
+ const Crypto = exports.Crypto = {
20
+ getPublicKey() {
21
+ return (0, _bridge.getNativeModule)().getPublicKey();
22
+ },
23
+ /**
24
+ * Derives a shared encryption key natively without returning it to JavaScript.
25
+ * Call `encryptBySharedKey` / `decryptBySharedKey` afterward (legacy bridge methods).
26
+ */
27
+ establishSharedKey(serverPublicKey, options) {
28
+ const native = (0, _bridge.getNativeModule)();
29
+ const nativeOptions = toNativeCryptoOptions(options);
30
+ if (options?.returnSharedKey) {
31
+ return native.getSharedKey(serverPublicKey, nativeOptions);
32
+ }
33
+ if (typeof native.establishSharedKey === 'function') {
34
+ return native.establishSharedKey(serverPublicKey, nativeOptions);
35
+ }
36
+ return native.getSharedKey(serverPublicKey, nativeOptions).then(() => undefined);
37
+ }
38
+ };
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_bridge","require","toNativeCryptoOptions","options","keyAgreementAlgorithm","keyFactoryAlgorithm","keyType","encryptionKeyAlgorithm","hmacKeyAlgorithm","hmacAlgorithm","cipherTransformation","cipher","gcmTagLength","tagLength","gcmIvLength","ivLength","Crypto","exports","getPublicKey","getNativeModule","establishSharedKey","serverPublicKey","native","nativeOptions","returnSharedKey","getSharedKey","then","undefined"],"sourceRoot":"../../../src","sources":["crypto/index.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGA,SAASC,qBAAqBA,CAACC,OAA8B,EAAE;EAC7D,OAAO;IACLC,qBAAqB,EAAED,OAAO,EAAEC,qBAAqB,IAAI,QAAQ;IACjEC,mBAAmB,EAAEF,OAAO,EAAEG,OAAO,IAAIH,OAAO,EAAEE,mBAAmB,IAAI,KAAK;IAC9EE,sBAAsB,EAAEJ,OAAO,EAAEI,sBAAsB,IAAI,SAAS;IACpEC,gBAAgB,EAAEL,OAAO,EAAEM,aAAa,IAAIN,OAAO,EAAEK,gBAAgB,IAAI,cAAc;IACvFE,oBAAoB,EAAEP,OAAO,EAAEQ,MAAM,IAAIR,OAAO,EAAEO,oBAAoB,IAAI,SAAS;IACnFE,YAAY,EAAET,OAAO,EAAEU,SAAS,IAAIV,OAAO,EAAES,YAAY,IAAI,GAAG;IAChEE,WAAW,EAAEX,OAAO,EAAEY,QAAQ,IAAIZ,OAAO,EAAEW,WAAW,IAAI;EAC5D,CAAC;AACH;AAOO,MAAME,MAAM,GAAAC,OAAA,CAAAD,MAAA,GAAG;EACpBE,YAAYA,CAAA,EAAoB;IAC9B,OAAO,IAAAC,uBAAe,EAAC,CAAC,CAACD,YAAY,CAAC,CAAC;EACzC,CAAC;EAED;AACF;AACA;AACA;EACEE,kBAAkBA,CAChBC,eAAuB,EACvBlB,OAAmC,EACX;IACxB,MAAMmB,MAAM,GAAG,IAAAH,uBAAe,EAAC,CAAC;IAChC,MAAMI,aAAa,GAAGrB,qBAAqB,CAACC,OAAO,CAAC;IAEpD,IAAIA,OAAO,EAAEqB,eAAe,EAAE;MAC5B,OAAOF,MAAM,CAACG,YAAY,CAACJ,eAAe,EAAEE,aAAa,CAAC;IAC5D;IAEA,IAAI,OAAOD,MAAM,CAACF,kBAAkB,KAAK,UAAU,EAAE;MACnD,OAAOE,MAAM,CAACF,kBAAkB,CAACC,eAAe,EAAEE,aAAa,CAAC;IAClE;IAEA,OAAOD,MAAM,CAACG,YAAY,CAACJ,eAAe,EAAEE,aAAa,CAAC,CAACG,IAAI,CAAC,MAAMC,SAAS,CAAC;EAClF;AACF,CAAC","ignoreList":[]}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DeviceSecurity = void 0;
7
+ var _reactNative = require("react-native");
8
+ var _bridge = require("../native/bridge.js");
9
+ function parseEnvironment(raw) {
10
+ return {
11
+ isEmulator: Boolean(raw.isEmulator),
12
+ isSimulator: Boolean(raw.isSimulator),
13
+ indicators: Array.isArray(raw.indicators) ? raw.indicators.filter(item => typeof item === 'string') : []
14
+ };
15
+ }
16
+ const DeviceSecurity = exports.DeviceSecurity = {
17
+ /** @deprecated Use `isCompromised()` or `SecuritySuite.getSecurityReport()`. */
18
+ hasSecurityRisk() {
19
+ return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
20
+ },
21
+ isCompromised() {
22
+ return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
23
+ },
24
+ isRooted() {
25
+ if (_reactNative.Platform.OS !== 'android') {
26
+ return Promise.resolve(false);
27
+ }
28
+ return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
29
+ },
30
+ isJailbroken() {
31
+ if (_reactNative.Platform.OS !== 'ios') {
32
+ return Promise.resolve(false);
33
+ }
34
+ return (0, _bridge.getNativeModule)().deviceHasSecurityRisk();
35
+ },
36
+ getEnvironment() {
37
+ return (0, _bridge.getNativeModule)().deviceGetEnvironment().then(result => parseEnvironment(result));
38
+ }
39
+ };
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","_bridge","parseEnvironment","raw","isEmulator","Boolean","isSimulator","indicators","Array","isArray","filter","item","DeviceSecurity","exports","hasSecurityRisk","getNativeModule","deviceHasSecurityRisk","isCompromised","isRooted","Platform","OS","Promise","resolve","isJailbroken","getEnvironment","deviceGetEnvironment","then","result"],"sourceRoot":"../../../src","sources":["device/index.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,IAAAC,OAAA,GAAAD,OAAA;AAGA,SAASE,gBAAgBA,CAACC,GAA4B,EAAqB;EACzE,OAAO;IACLC,UAAU,EAAEC,OAAO,CAACF,GAAG,CAACC,UAAU,CAAC;IACnCE,WAAW,EAAED,OAAO,CAACF,GAAG,CAACG,WAAW,CAAC;IACrCC,UAAU,EAAEC,KAAK,CAACC,OAAO,CAACN,GAAG,CAACI,UAAU,CAAC,GACrCJ,GAAG,CAACI,UAAU,CAACG,MAAM,CAAEC,IAAI,IAAqB,OAAOA,IAAI,KAAK,QAAQ,CAAC,GACzE;EACN,CAAC;AACH;AAEO,MAAMC,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAG;EAC5B;EACAE,eAAeA,CAAA,EAAqB;IAClC,OAAO,IAAAC,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDC,aAAaA,CAAA,EAAqB;IAChC,OAAO,IAAAF,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDE,QAAQA,CAAA,EAAqB;IAC3B,IAAIC,qBAAQ,CAACC,EAAE,KAAK,SAAS,EAAE;MAC7B,OAAOC,OAAO,CAACC,OAAO,CAAC,KAAK,CAAC;IAC/B;IACA,OAAO,IAAAP,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDO,YAAYA,CAAA,EAAqB;IAC/B,IAAIJ,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACzB,OAAOC,OAAO,CAACC,OAAO,CAAC,KAAK,CAAC;IAC/B;IACA,OAAO,IAAAP,uBAAe,EAAC,CAAC,CAACC,qBAAqB,CAAC,CAAC;EAClD,CAAC;EAEDQ,cAAcA,CAAA,EAA+B;IAC3C,OAAO,IAAAT,uBAAe,EAAC,CAAC,CACrBU,oBAAoB,CAAC,CAAC,CACtBC,IAAI,CAAEC,MAAM,IAAKzB,gBAAgB,CAACyB,MAAM,CAAC,CAAC;EAC/C;AACF,CAAC","ignoreList":[]}