react-native-instantpay-code-push 1.2.7 → 2.0.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
CHANGED
|
@@ -126,7 +126,7 @@ class MainApplication : Application(), ReactApplication {
|
|
|
126
126
|
Open modify your ```AppDelegate.swift:```
|
|
127
127
|
|
|
128
128
|
```
|
|
129
|
-
import
|
|
129
|
+
import InstantpayCodePush //👈 import this package
|
|
130
130
|
|
|
131
131
|
@main
|
|
132
132
|
class AppDelegate: RCTAppDelegate {
|
|
@@ -137,7 +137,7 @@ class AppDelegate: RCTAppDelegate {
|
|
|
137
137
|
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
138
138
|
#else
|
|
139
139
|
Bundle.main.url(forResource: "main", withExtension: "jsbundle") //❌ remove this
|
|
140
|
-
|
|
140
|
+
InstantpayCodePush.bundleURL() //👈 add this
|
|
141
141
|
#endif
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -170,7 +170,46 @@ import React
|
|
|
170
170
|
}
|
|
171
171
|
fileUrl = url
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
//Verify Bundle Url Signature
|
|
175
|
+
let decryptSignatureData = SignatureVerifier.decryptSignatureUrl(bundleUrl: fileUrlString)
|
|
176
|
+
|
|
177
|
+
if (decryptSignatureData == nil){
|
|
178
|
+
let error = NSError(domain: "IpayCodePush", code: 0,
|
|
179
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid 'fileUrl' Signature: \(String(describing: fileUrl))"])
|
|
180
|
+
reject("INVALID_URL_SIGNATURE", error.localizedDescription, error)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Convert String → Data
|
|
185
|
+
if let jsonData = decryptSignatureData!.data(using: .utf8) {
|
|
186
|
+
|
|
187
|
+
do {
|
|
188
|
+
let json = try JSONSerialization.jsonObject(
|
|
189
|
+
with: jsonData,
|
|
190
|
+
options: []
|
|
191
|
+
)
|
|
173
192
|
|
|
193
|
+
if let dictionary = json as? [String: Any] {
|
|
194
|
+
|
|
195
|
+
guard let url = URL(string: dictionary["bundleUrl"] as! String) else {
|
|
196
|
+
let error = NSError(domain: "IpayCodePush", code: 0,
|
|
197
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid bundle 'fileUrl' provided: \(dictionary["bundleUrl"] as! String)"])
|
|
198
|
+
reject("INVALID_FILE_URL", error.localizedDescription, error)
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
fileUrl = url
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
let error = NSError(domain: "IpayCodePush", code: 0,
|
|
205
|
+
userInfo: [NSLocalizedDescriptionKey: "JSON parse error: Signature"])
|
|
206
|
+
reject("INVALID_URL_SIGNATURE", error.localizedDescription, error)
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "Bundle Url signature is valid : \(String(describing: fileUrl))")
|
|
212
|
+
|
|
174
213
|
// Extract fileHash if provided
|
|
175
214
|
let fileHash = data["fileHash"] as? String
|
|
176
215
|
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
9
|
import Security
|
|
10
|
+
import CommonCrypto
|
|
11
|
+
import LocalAuthentication
|
|
10
12
|
|
|
11
13
|
/// Prefix for signed file hash format.
|
|
12
14
|
private let SIGNED_HASH_PREFIX = "sig:"
|
|
@@ -451,7 +453,127 @@ public class SignatureVerifier {
|
|
|
451
453
|
return nil
|
|
452
454
|
}
|
|
453
455
|
|
|
454
|
-
return data.base64EncodedString()
|
|
456
|
+
//return data.base64EncodedString()
|
|
457
|
+
|
|
458
|
+
// 🔥 Wrap PKCS1 key into SPKI format
|
|
459
|
+
let spkiHeader: [UInt8] = [
|
|
460
|
+
0x30, 0x82, 0x01, 0x22,
|
|
461
|
+
0x30, 0x0D,
|
|
462
|
+
0x06, 0x09,
|
|
463
|
+
0x2A, 0x86, 0x48, 0x86,
|
|
464
|
+
0xF7, 0x0D, 0x01, 0x01,
|
|
465
|
+
0x01, 0x05, 0x00,
|
|
466
|
+
0x03, 0x82, 0x01, 0x0F,
|
|
467
|
+
0x00
|
|
468
|
+
]
|
|
469
|
+
|
|
470
|
+
var spkiData = Data(spkiHeader)
|
|
471
|
+
spkiData.append(data)
|
|
472
|
+
|
|
473
|
+
return spkiData.base64EncodedString()
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// MARK: - Hybrid Decrypt (RSA + AES)
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Decrypt signature from bundle Url.
|
|
480
|
+
* @param bundleUrl The signed url
|
|
481
|
+
* @return plan Url to downlaod Bundle
|
|
482
|
+
*/
|
|
483
|
+
public static func decryptSignatureUrl(bundleUrl: String) -> String? {
|
|
484
|
+
|
|
485
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "Signed Bundle URL: \(bundleUrl)")
|
|
486
|
+
|
|
487
|
+
var queryParameters: [String: String] = [:]
|
|
488
|
+
|
|
489
|
+
// 1. Create a URL object from the string.
|
|
490
|
+
if let url = URL(string: bundleUrl),
|
|
491
|
+
// 2. Create a URLComponents object from the URL.
|
|
492
|
+
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
493
|
+
// 3. Access the array of URLQueryItem objects.
|
|
494
|
+
let queryItems = urlComponents.queryItems {
|
|
495
|
+
|
|
496
|
+
// 4. Iterate through the query items and populate the dictionary.
|
|
497
|
+
for item in queryItems {
|
|
498
|
+
queryParameters[item.name] = item.value
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "Parsed URL: \(queryParameters)")
|
|
503
|
+
|
|
504
|
+
if(queryParameters.isEmpty){
|
|
505
|
+
return nil
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
let signatureData = queryParameters["signatureData"]!
|
|
509
|
+
|
|
510
|
+
let encryptedKey = queryParameters["itemData"]!
|
|
511
|
+
|
|
512
|
+
let rawIv = queryParameters["raw"]!
|
|
513
|
+
|
|
514
|
+
guard let privateKey = getPrivateKey() else {
|
|
515
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "Private key not found to decrypt")
|
|
516
|
+
return nil
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
//Decode Base64
|
|
520
|
+
guard let encryptedKeyData = Data(base64Encoded: encryptedKey),
|
|
521
|
+
let ivData = Data(base64Encoded: rawIv),
|
|
522
|
+
let encryptedData = Data(base64Encoded: signatureData)
|
|
523
|
+
else {
|
|
524
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "Base64 decode failed in decryptSignatureUrl")
|
|
525
|
+
return nil
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
//RSA Decrypt AES Key (PKCS1)
|
|
529
|
+
var error: Unmanaged<CFError>?
|
|
530
|
+
guard let aesKeyData = SecKeyCreateDecryptedData(
|
|
531
|
+
privateKey,
|
|
532
|
+
.rsaEncryptionPKCS1,
|
|
533
|
+
encryptedKeyData as CFData,
|
|
534
|
+
&error
|
|
535
|
+
) as Data? else {
|
|
536
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "RSA decrypt failed: \(error?.takeRetainedValue() as Any)")
|
|
537
|
+
return nil
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
//AES-256-CBC Decrypt
|
|
541
|
+
guard aesKeyData.count == 32 else {
|
|
542
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "AES key must be 32 bytes (256-bit)")
|
|
543
|
+
return nil
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
var outLength = Int(0)
|
|
547
|
+
let outputData = NSMutableData(length: encryptedData.count + kCCBlockSizeAES128)!
|
|
548
|
+
|
|
549
|
+
let status = encryptedData.withUnsafeBytes { encryptedBytes in
|
|
550
|
+
aesKeyData.withUnsafeBytes { keyBytes in
|
|
551
|
+
ivData.withUnsafeBytes { ivBytes in
|
|
552
|
+
|
|
553
|
+
CCCrypt(
|
|
554
|
+
CCOperation(kCCDecrypt),
|
|
555
|
+
CCAlgorithm(kCCAlgorithmAES),
|
|
556
|
+
CCOptions(kCCOptionPKCS7Padding),
|
|
557
|
+
keyBytes.baseAddress,
|
|
558
|
+
aesKeyData.count,
|
|
559
|
+
ivBytes.baseAddress,
|
|
560
|
+
encryptedBytes.baseAddress,
|
|
561
|
+
encryptedData.count,
|
|
562
|
+
outputData.mutableBytes,
|
|
563
|
+
outputData.length,
|
|
564
|
+
&outLength
|
|
565
|
+
)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if status == kCCSuccess {
|
|
571
|
+
outputData.length = outLength
|
|
572
|
+
return String(data: outputData as Data, encoding: .utf8)
|
|
573
|
+
} else {
|
|
574
|
+
IpayCodePushHelper.logPrint(classTag: CLASS_TAG, log: "AES decrypt failed: \(status)")
|
|
575
|
+
return nil
|
|
576
|
+
}
|
|
455
577
|
}
|
|
456
578
|
|
|
457
579
|
}
|