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 IpayCodePush //👈 import this package
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
- IpayCodePush.bundleURL() //👈 add this
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-instantpay-code-push",
3
- "version": "1.2.7",
3
+ "version": "2.0.0",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",