react-native-security-suite 0.7.2 → 0.8.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 +5 -15
- package/ios/PulseUINotification.swift +82 -0
- package/ios/SecuritySuite.swift +33 -80
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcuserdata/m.navabifar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/SecuritySuite.xcodeproj/xcuserdata/m.navabifar.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/ios/SslPinning.swift +163 -58
- package/ios/StorageEncryption.swift +8 -0
- package/package.json +1 -1
- package/react-native-security-suite.podspec +2 -0
package/README.md
CHANGED
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
Security solutions for React Native both platform Android and iOS
|
|
4
4
|
You can use any of the following:
|
|
5
5
|
|
|
6
|
-
<ol>
|
|
7
|
-
<li>Android Root device or iOS Jailbreak device detection</li>
|
|
8
|
-
<li>Text Encryption/Decryption</li>
|
|
9
|
-
<li>Secure storage</li>
|
|
10
|
-
<li>Diffie–Hellman key exchange</li>
|
|
11
|
-
<li>SSL Pinning & public key pinning</li>
|
|
12
|
-
</ol>
|
|
6
|
+
<ol><li>Android Root device or iOS Jailbreak device detection</li><li>Text Encryption/Decryption</li><li>Secure storage</li><li>Diffie–Hellman key exchange</li><li>SSL Pinning & public key pinning</li><li>Network Logger (Android Chucker - iOS Pulse)</li></ol>
|
|
13
7
|
|
|
14
8
|
## Installation
|
|
15
9
|
|
|
@@ -32,8 +26,7 @@ const isRiskyDevice = await deviceHasSecurityRisk();
|
|
|
32
26
|
console.log('Root/Jailbreak detection result: ', isRiskyDevice);
|
|
33
27
|
```
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
2. Text Encryption/Decryption example:
|
|
29
|
+
2\. Text Encryption/Decryption example:
|
|
37
30
|
|
|
38
31
|
```js
|
|
39
32
|
const softEncrypted = await encrypt('STR_FOR_ENCRYPT');
|
|
@@ -42,8 +35,7 @@ const softDecrypted = await decrypt('STR_FOR_DECRYPT');
|
|
|
42
35
|
console.log('Decrypted result: ', softDecrypted);
|
|
43
36
|
```
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
3. Secure storage example:
|
|
38
|
+
3\. Secure storage example:
|
|
47
39
|
|
|
48
40
|
```js
|
|
49
41
|
import { SecureStorage } from 'react-native-security-suite';
|
|
@@ -52,8 +44,7 @@ SecureStorage.setItem('key', 'value');
|
|
|
52
44
|
console.log(await SecureStorage.getItem('key'));
|
|
53
45
|
```
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
4. Diffie–Hellman key exchange:
|
|
47
|
+
4\. Diffie–Hellman key exchange:
|
|
57
48
|
|
|
58
49
|
```js
|
|
59
50
|
import {
|
|
@@ -80,8 +71,7 @@ const hardDecrypted = await decryptBySharedKey('STR_FOR_DECRYPT');
|
|
|
80
71
|
console.log('Decrypted result: ', hardDecrypted);
|
|
81
72
|
```
|
|
82
73
|
|
|
83
|
-
|
|
84
|
-
5. SSL Pinning with android network logger example:
|
|
74
|
+
5\. SSL Pinning with network logger:
|
|
85
75
|
|
|
86
76
|
```js
|
|
87
77
|
import { fetch } from 'react-native-security-suite';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import UserNotifications
|
|
2
|
+
import PulseUI
|
|
3
|
+
import SwiftUI
|
|
4
|
+
|
|
5
|
+
@available(iOS 13.0, *)
|
|
6
|
+
class PulseUINotification: NSObject, UNUserNotificationCenterDelegate {
|
|
7
|
+
override init() {
|
|
8
|
+
super.init()
|
|
9
|
+
|
|
10
|
+
UNUserNotificationCenter.current().delegate = self
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
14
|
+
completionHandler([.sound, .badge])
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
18
|
+
switch response.actionIdentifier {
|
|
19
|
+
case "openPulse":
|
|
20
|
+
openPulseUI()
|
|
21
|
+
break
|
|
22
|
+
default:
|
|
23
|
+
openPulseUI()
|
|
24
|
+
break
|
|
25
|
+
}
|
|
26
|
+
completionHandler()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func requestPermission() {
|
|
30
|
+
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
|
31
|
+
if granted {
|
|
32
|
+
// print("PulseUI Notification Permission Granted")
|
|
33
|
+
} else {
|
|
34
|
+
print("PulseUI Notification Permission Denied")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func showNotification(body: String) {
|
|
40
|
+
requestPermission()
|
|
41
|
+
|
|
42
|
+
let content = UNMutableNotificationContent()
|
|
43
|
+
content.title = "Recording HTTP Activity"
|
|
44
|
+
content.body = body
|
|
45
|
+
content.sound = .none
|
|
46
|
+
content.badge = 1
|
|
47
|
+
|
|
48
|
+
let action = UNNotificationAction(identifier: "openPulse", title: "Open Pulse", options: [])
|
|
49
|
+
let category = UNNotificationCategory(identifier: "pulseCategory", actions: [action], intentIdentifiers: [], options: [])
|
|
50
|
+
|
|
51
|
+
UNUserNotificationCenter.current().setNotificationCategories([category])
|
|
52
|
+
content.categoryIdentifier = "pulseCategory"
|
|
53
|
+
|
|
54
|
+
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
|
55
|
+
|
|
56
|
+
let request = UNNotificationRequest(identifier: "pulseNotification", content: content, trigger: trigger)
|
|
57
|
+
|
|
58
|
+
UNUserNotificationCenter.current().add(request) { error in
|
|
59
|
+
if let error = error {
|
|
60
|
+
print("Error scheduling notification: \(error.localizedDescription)")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@objc(openPulseUI)
|
|
66
|
+
func openPulseUI() {
|
|
67
|
+
DispatchQueue.main.async {
|
|
68
|
+
if #available(iOS 15.0, *) {
|
|
69
|
+
let hostingController = UIHostingController(rootView: ConsoleView())
|
|
70
|
+
if let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
|
|
71
|
+
let navigationController = UINavigationController()
|
|
72
|
+
navigationController.setViewControllers([hostingController], animated: false)
|
|
73
|
+
rootViewController.present(navigationController, animated: true, completion: nil)
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
if let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
|
|
77
|
+
rootViewController.present(UIViewController(), animated: true, completion: nil)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
package/ios/SecuritySuite.swift
CHANGED
|
@@ -11,8 +11,9 @@ class SecuritySuite: NSObject {
|
|
|
11
11
|
var privateKey: String!,
|
|
12
12
|
publicKey: String!,
|
|
13
13
|
sharedKey: String!,
|
|
14
|
-
keyData: Data
|
|
15
|
-
|
|
14
|
+
keyData: Data!,
|
|
15
|
+
session: URLSession!
|
|
16
|
+
|
|
16
17
|
@objc(getPublicKey:withRejecter:)
|
|
17
18
|
func getPublicKey(resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
|
|
18
19
|
do {
|
|
@@ -127,62 +128,43 @@ class SecuritySuite: NSObject {
|
|
|
127
128
|
|
|
128
129
|
@objc(fetch:withData:withCallback:)
|
|
129
130
|
func fetch(url: NSString, data: NSDictionary, callback: @escaping RCTResponseSenderBlock) -> Void {
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
let sslPinning = SSLPinning(data: data)
|
|
131
|
+
let configuration = URLSessionConfiguration.default
|
|
132
|
+
configuration.httpShouldSetCookies = false
|
|
133
|
+
configuration.httpCookieAcceptPolicy = .never
|
|
134
|
+
configuration.networkServiceType = .responsiveData
|
|
135
|
+
configuration.shouldUseExtendedBackgroundIdleMode = true
|
|
137
136
|
|
|
138
|
-
let startTime = Date()
|
|
139
137
|
var request = URLRequest(url: URL(string: url as String)!)
|
|
140
|
-
|
|
141
138
|
request.httpMethod = data["method"] as? String ?? "POST"
|
|
142
139
|
request.timeoutInterval = data["timeout"] as? TimeInterval ?? 60.0
|
|
143
140
|
request.allHTTPHeaderFields = data["headers"] as? [String : String]
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let session = URLSession(configuration: config, delegate: sslPinning, delegateQueue: .main)
|
|
151
|
-
|
|
152
|
-
let task = session.dataTask(with: request) { data, response, error in
|
|
153
|
-
let response = response as? HTTPURLResponse
|
|
154
|
-
|
|
155
|
-
if error == nil {
|
|
156
|
-
let responseCode = response?.statusCode
|
|
157
|
-
let responseString = String.init(decoding: data ?? .init(), as: UTF8.self)
|
|
158
|
-
let errorString = error?.localizedDescription
|
|
159
|
-
let responseJSON = try? JSONSerialization.jsonObject(with: data!, options: [])
|
|
160
|
-
|
|
161
|
-
var result:NSMutableDictionary = [
|
|
162
|
-
"status": response?.statusCode,
|
|
163
|
-
"url": url,
|
|
164
|
-
]
|
|
165
|
-
if errorString == nil && responseCode! < 400 {
|
|
166
|
-
result["response"] = responseString
|
|
167
|
-
result["duration"] = "\(Int(Date().timeIntervalSince(startTime) * 1000))ms"
|
|
168
|
-
result["responseJSON"] = responseJSON
|
|
169
|
-
callback([result, NSNull()])
|
|
170
|
-
} else {
|
|
171
|
-
result["error"] = responseString
|
|
172
|
-
result["errorJSON"] = responseJSON
|
|
173
|
-
do {
|
|
174
|
-
let jsonData = try JSONSerialization.data(withJSONObject: result)
|
|
175
|
-
callback([NSNull(), result])
|
|
176
|
-
} catch {
|
|
177
|
-
callback([NSNull(), "JSON_PARSE_ERROR"])
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
callback([NSNull(), "MUST_BE_UPDATE"])
|
|
141
|
+
// Prepare body
|
|
142
|
+
if let body = data["body"] {
|
|
143
|
+
if let bodyString = body as? String {
|
|
144
|
+
request.httpBody = bodyString.data(using: .utf8)
|
|
145
|
+
} else if let bodyDict = body as? [String: Any] {
|
|
146
|
+
request.httpBody = try? JSONSerialization.data(withJSONObject: bodyDict, options: [])
|
|
182
147
|
}
|
|
183
148
|
}
|
|
184
|
-
|
|
185
|
-
|
|
149
|
+
if data["keyId"] != nil && data["requestId"] != nil {
|
|
150
|
+
request
|
|
151
|
+
.setValue(
|
|
152
|
+
jwsHeader(
|
|
153
|
+
payload: request.httpBody ?? .init(),
|
|
154
|
+
keyId: data["keyId"] as! String,
|
|
155
|
+
requestId: data["requestId"] as! String
|
|
156
|
+
),
|
|
157
|
+
forHTTPHeaderField: "X-JWS-Signature"
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let sslPinning = SSLPinning(url: url, data: data, callback: callback)
|
|
162
|
+
let session = URLSession(
|
|
163
|
+
configuration: configuration,
|
|
164
|
+
delegate: sslPinning,
|
|
165
|
+
delegateQueue: .main
|
|
166
|
+
)
|
|
167
|
+
session.dataTask(with: request).resume()
|
|
186
168
|
}
|
|
187
169
|
|
|
188
170
|
@objc(deviceHasSecurityRisk:withRejecter:)
|
|
@@ -223,32 +205,3 @@ class SecuritySuite: NSObject {
|
|
|
223
205
|
}
|
|
224
206
|
}
|
|
225
207
|
}
|
|
226
|
-
|
|
227
|
-
struct ASN1 {
|
|
228
|
-
static let rsa2048 = Data(base64Encoded: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A")!
|
|
229
|
-
static let rsa4096 = Data(base64Encoded: "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A")!
|
|
230
|
-
static let ec256 = Data(base64Encoded: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgA=")!
|
|
231
|
-
static let ec384 = Data(base64Encoded: "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgA=")!
|
|
232
|
-
static let ec521 = Data(base64Encoded: "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQ=")!
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
struct JoseHeader: Codable {
|
|
236
|
-
internal init(alg: String = "HS256", kid: String, b64: Bool = false, crit: [String] = ["b64"], requestId: String) {
|
|
237
|
-
self.alg = alg
|
|
238
|
-
self.kid = kid
|
|
239
|
-
self.b64 = b64
|
|
240
|
-
self.crit = crit
|
|
241
|
-
self.requestId = requestId
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
private enum CodingKeys: String, CodingKey {
|
|
245
|
-
case alg, kid, b64, crit
|
|
246
|
-
case requestId = "request_id"
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
let alg: String
|
|
250
|
-
let kid: String
|
|
251
|
-
let b64: Bool
|
|
252
|
-
let crit: [String]
|
|
253
|
-
let requestId: String
|
|
254
|
-
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>SecuritySuite.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
package/ios/SslPinning.swift
CHANGED
|
@@ -1,71 +1,155 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Pulse
|
|
2
2
|
|
|
3
3
|
@available(iOS 13.0, *)
|
|
4
|
-
class SSLPinning: NSObject,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
}
|
|
16
42
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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)
|
|
22
49
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
|
|
51
|
+
guard let trust = challenge.protectionSpace.serverTrust else {
|
|
52
|
+
return completionHandler(.cancelAuthenticationChallenge, nil)
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
|
64
|
+
}
|
|
65
|
+
let intermediateCertificatesValid = trust.certificates.dropFirst().prefix(2).allSatisfy {
|
|
66
|
+
($0.pin.map(intermediateKeyHashes.contains) ?? false)
|
|
67
|
+
}
|
|
68
|
+
let leafCertificateValid = leafKeyHashes.contains(
|
|
69
|
+
leafCertificate.pin ?? .init()
|
|
70
|
+
)
|
|
28
71
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
($0.pin.map(intermediateKeyHashes.contains) ?? false)
|
|
40
|
-
}
|
|
41
|
-
let leafCertificateValid = leafKeyHashes.contains(leafCertificate.pin ?? .init())
|
|
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
|
|
81
|
+
}
|
|
42
82
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let commonNameRegex = try? NSRegularExpression(pattern: pattern) else {
|
|
48
|
-
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
49
|
-
return
|
|
50
|
-
}
|
|
83
|
+
guard commonNameRegex.textMatches(in: host) == [host] else {
|
|
84
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
51
87
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
96
|
+
}
|
|
56
97
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
let credential = URLCredential(trust: trust)
|
|
99
|
+
completionHandler(.useCredential, credential)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) {
|
|
103
|
+
networkLogger.logTaskCreated(task)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
107
|
+
networkLogger.logDataTask(dataTask, didReceive: data)
|
|
108
|
+
responseData.append(data)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
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) ?? "",
|
|
128
|
+
]])
|
|
129
|
+
} else {
|
|
130
|
+
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: []),
|
|
136
|
+
], NSNull()])
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
callback([NSNull(), "Unknown error occurred"])
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
|
145
|
+
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)")
|
|
61
150
|
}
|
|
62
|
-
|
|
63
|
-
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
64
|
-
return
|
|
151
|
+
}
|
|
65
152
|
}
|
|
66
|
-
|
|
67
|
-
let credential = URLCredential(trust: trust)
|
|
68
|
-
completionHandler(.useCredential, credential)
|
|
69
153
|
}
|
|
70
154
|
}
|
|
71
155
|
|
|
@@ -196,3 +280,24 @@ extension NSRegularExpression {
|
|
|
196
280
|
}
|
|
197
281
|
}
|
|
198
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"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
let alg: String
|
|
299
|
+
let kid: String
|
|
300
|
+
let b64: Bool
|
|
301
|
+
let crit: [String]
|
|
302
|
+
let requestId: String
|
|
303
|
+
}
|
|
@@ -79,3 +79,11 @@ public extension Data {
|
|
|
79
79
|
.joined()
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
struct ASN1 {
|
|
84
|
+
static let rsa2048 = Data(base64Encoded: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A")!
|
|
85
|
+
static let rsa4096 = Data(base64Encoded: "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A")!
|
|
86
|
+
static let ec256 = Data(base64Encoded: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgA=")!
|
|
87
|
+
static let ec384 = Data(base64Encoded: "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgA=")!
|
|
88
|
+
static let ec521 = Data(base64Encoded: "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQ=")!
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -18,6 +18,8 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
|
|
19
19
|
s.dependency "React-Core"
|
|
20
20
|
s.dependency "IOSSecuritySuite"
|
|
21
|
+
s.dependency "PulseCore"
|
|
22
|
+
s.dependency "PulseUI"
|
|
21
23
|
|
|
22
24
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
23
25
|
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|