@capgo/capacitor-updater 6.11.2 → 6.12.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.
@@ -6,6 +6,7 @@
6
6
 
7
7
  import Foundation
8
8
  import CommonCrypto
9
+ import zlib
9
10
 
10
11
  ///
11
12
  /// Constants
@@ -209,3 +210,79 @@ fileprivate extension SecKey {
209
210
  return SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, nil)
210
211
  }
211
212
  }
213
+
214
+ public struct CryptoCipher {
215
+ public static func calcChecksum(filePath: URL) -> String {
216
+ let bufferSize = 1024 * 1024 * 5 // 5 MB
217
+ var checksum = uLong(0)
218
+
219
+ do {
220
+ let fileHandle = try FileHandle(forReadingFrom: filePath)
221
+ defer {
222
+ fileHandle.closeFile()
223
+ }
224
+
225
+ while autoreleasepool(invoking: {
226
+ let fileData = fileHandle.readData(ofLength: bufferSize)
227
+ if fileData.count > 0 {
228
+ checksum = fileData.withUnsafeBytes {
229
+ crc32(checksum, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count))
230
+ }
231
+ return true // Continue
232
+ } else {
233
+ return false // End of file
234
+ }
235
+ }) {}
236
+
237
+ return String(format: "%08X", checksum).lowercased()
238
+ } catch {
239
+ print("\(CapacitorUpdater.TAG) Cannot get checksum: \(filePath.path)", error)
240
+ return ""
241
+ }
242
+ }
243
+ public static func decryptFile(filePath: URL, privateKey: String, sessionKey: String, version: String) throws {
244
+ if privateKey.isEmpty {
245
+ print("\(CapacitorUpdater.TAG) Cannot found privateKey")
246
+ return
247
+ } else if sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
248
+ print("\(CapacitorUpdater.TAG) Cannot found sessionKey")
249
+ return
250
+ }
251
+ do {
252
+ guard let rsaPrivateKey: RSAPrivateKey = .load(rsaPrivateKey: privateKey) else {
253
+ print("cannot decode privateKey", privateKey)
254
+ throw CustomError.cannotDecode
255
+ }
256
+
257
+ let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
258
+ guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
259
+ print("cannot decode sessionKey", sessionKey)
260
+ throw CustomError.cannotDecode
261
+ }
262
+
263
+ guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
264
+ throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
265
+ }
266
+
267
+ guard let sessionKeyDataDecrypted = rsaPrivateKey.decrypt(data: sessionKeyDataEncrypted) else {
268
+ throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
269
+ }
270
+
271
+ let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
272
+
273
+ guard let encryptedData = try? Data(contentsOf: filePath) else {
274
+ throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
275
+ }
276
+
277
+ guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
278
+ throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
279
+ }
280
+
281
+ try decryptedData.write(to: filePath)
282
+
283
+ } catch {
284
+ print("\(CapacitorUpdater.TAG) Cannot decode: \(filePath.path)", error)
285
+ throw CustomError.cannotDecode
286
+ }
287
+ }
288
+ }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import Foundation
8
8
  import CommonCrypto
9
+ import CryptoKit
9
10
 
10
11
  ///
11
12
  /// Constants
@@ -203,3 +204,96 @@ fileprivate extension SecKey {
203
204
  return SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, nil)
204
205
  }
205
206
  }
207
+
208
+ public struct CryptoCipherV2 {
209
+
210
+ public static func decryptChecksum(checksum: String, publicKey: String, version: String) throws -> String {
211
+ if publicKey.isEmpty {
212
+ return checksum
213
+ }
214
+ do {
215
+ let checksumBytes: Data = Data(base64Encoded: checksum)!
216
+ guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: publicKey) else {
217
+ print("cannot decode publicKey", publicKey)
218
+ throw CustomError.cannotDecode
219
+ }
220
+ guard let decryptedChecksum = rsaPublicKey.decrypt(data: checksumBytes) else {
221
+ throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
222
+ }
223
+ return decryptedChecksum.base64EncodedString()
224
+ } catch {
225
+ print("\(CapacitorUpdater.TAG) Cannot decrypt checksum: \(checksum)", error)
226
+ throw CustomError.cannotDecode
227
+ }
228
+ }
229
+ public static func calcChecksum(filePath: URL) -> String {
230
+ let bufferSize = 1024 * 1024 * 5 // 5 MB
231
+ var sha256 = SHA256()
232
+
233
+ do {
234
+ let fileHandle = try FileHandle(forReadingFrom: filePath)
235
+ defer {
236
+ fileHandle.closeFile()
237
+ }
238
+
239
+ while autoreleasepool(invoking: {
240
+ let fileData = fileHandle.readData(ofLength: bufferSize)
241
+ if fileData.count > 0 {
242
+ sha256.update(data: fileData)
243
+ return true // Continue
244
+ } else {
245
+ return false // End of file
246
+ }
247
+ }) {}
248
+
249
+ let digest = sha256.finalize()
250
+ return digest.compactMap { String(format: "%02x", $0) }.joined()
251
+ } catch {
252
+ print("\(CapacitorUpdater.TAG) Cannot get checksum: \(filePath.path)", error)
253
+ return ""
254
+ }
255
+ }
256
+
257
+ public static func decryptFile(filePath: URL, publicKey: String, sessionKey: String, version: String) throws {
258
+ if publicKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
259
+ print("\(CapacitorUpdater.TAG) Cannot find public key or sessionKey")
260
+ return
261
+ }
262
+ do {
263
+ guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: publicKey) else {
264
+ print("cannot decode publicKey", publicKey)
265
+ throw CustomError.cannotDecode
266
+ }
267
+
268
+ let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
269
+ guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
270
+ print("cannot decode sessionKey", sessionKey)
271
+ throw CustomError.cannotDecode
272
+ }
273
+
274
+ guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
275
+ throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
276
+ }
277
+
278
+ guard let sessionKeyDataDecrypted = rsaPublicKey.decrypt(data: sessionKeyDataEncrypted) else {
279
+ throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
280
+ }
281
+
282
+ let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
283
+
284
+ guard let encryptedData = try? Data(contentsOf: filePath) else {
285
+ throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
286
+ }
287
+
288
+ guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
289
+ throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
290
+ }
291
+
292
+ try decryptedData.write(to: filePath)
293
+
294
+ } catch {
295
+ print("\(CapacitorUpdater.TAG) Cannot decode: \(filePath.path)", error)
296
+ throw CustomError.cannotDecode
297
+ }
298
+ }
299
+ }
@@ -0,0 +1,258 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import Foundation
8
+
9
+ extension Collection {
10
+ subscript(safe index: Index) -> Element? {
11
+ return indices.contains(index) ? self[index] : nil
12
+ }
13
+ }
14
+ extension URL {
15
+ var isDirectory: Bool {
16
+ (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
17
+ }
18
+ var exist: Bool {
19
+ return FileManager().fileExists(atPath: self.path)
20
+ }
21
+ }
22
+ struct SetChannelDec: Decodable {
23
+ let status: String?
24
+ let error: String?
25
+ let message: String?
26
+ }
27
+ public class SetChannel: NSObject {
28
+ var status: String = ""
29
+ var error: String = ""
30
+ var message: String = ""
31
+ }
32
+ extension SetChannel {
33
+ func toDict() -> [String: Any] {
34
+ var dict: [String: Any] = [String: Any]()
35
+ let otherSelf: Mirror = Mirror(reflecting: self)
36
+ for child: Mirror.Child in otherSelf.children {
37
+ if let key: String = child.label {
38
+ dict[key] = child.value
39
+ }
40
+ }
41
+ return dict
42
+ }
43
+ }
44
+ struct GetChannelDec: Decodable {
45
+ let channel: String?
46
+ let status: String?
47
+ let error: String?
48
+ let message: String?
49
+ let allowSet: Bool?
50
+ }
51
+ public class GetChannel: NSObject {
52
+ var channel: String = ""
53
+ var status: String = ""
54
+ var error: String = ""
55
+ var message: String = ""
56
+ var allowSet: Bool = true
57
+ }
58
+ extension GetChannel {
59
+ func toDict() -> [String: Any] {
60
+ var dict: [String: Any] = [String: Any]()
61
+ let otherSelf: Mirror = Mirror(reflecting: self)
62
+ for child: Mirror.Child in otherSelf.children {
63
+ if let key: String = child.label {
64
+ dict[key] = child.value
65
+ }
66
+ }
67
+ return dict
68
+ }
69
+ }
70
+ struct InfoObject: Codable {
71
+ let platform: String?
72
+ let device_id: String?
73
+ let app_id: String?
74
+ let custom_id: String?
75
+ let version_build: String?
76
+ let version_code: String?
77
+ let version_os: String?
78
+ var version_name: String?
79
+ var old_version_name: String?
80
+ let plugin_version: String?
81
+ let is_emulator: Bool?
82
+ let is_prod: Bool?
83
+ var action: String?
84
+ var channel: String?
85
+ var defaultChannel: String?
86
+ }
87
+
88
+ public struct ManifestEntry: Codable {
89
+ let file_name: String?
90
+ let file_hash: String?
91
+ let download_url: String?
92
+ }
93
+
94
+ extension ManifestEntry {
95
+ func toDict() -> [String: Any] {
96
+ var dict: [String: Any] = [String: Any]()
97
+ let mirror = Mirror(reflecting: self)
98
+ for child in mirror.children {
99
+ if let key = child.label {
100
+ dict[key] = child.value
101
+ }
102
+ }
103
+ return dict
104
+ }
105
+ }
106
+
107
+ struct AppVersionDec: Decodable {
108
+ let version: String?
109
+ let checksum: String?
110
+ let url: String?
111
+ let message: String?
112
+ let error: String?
113
+ let session_key: String?
114
+ let major: Bool?
115
+ let data: [String: String]?
116
+ let manifest: [ManifestEntry]?
117
+ }
118
+
119
+ public class AppVersion: NSObject {
120
+ var version: String = ""
121
+ var checksum: String = ""
122
+ var url: String = ""
123
+ var message: String?
124
+ var error: String?
125
+ var sessionKey: String?
126
+ var major: Bool?
127
+ var data: [String: String]?
128
+ var manifest: [ManifestEntry]?
129
+ }
130
+
131
+ extension AppVersion {
132
+ func toDict() -> [String: Any] {
133
+ var dict: [String: Any] = [String: Any]()
134
+ let otherSelf: Mirror = Mirror(reflecting: self)
135
+ for child: Mirror.Child in otherSelf.children {
136
+ if let key: String = child.label {
137
+ if key == "manifest", let manifestEntries = child.value as? [ManifestEntry] {
138
+ dict[key] = manifestEntries.map { $0.toDict() }
139
+ } else {
140
+ dict[key] = child.value
141
+ }
142
+ }
143
+ }
144
+ return dict
145
+ }
146
+ }
147
+
148
+ extension OperatingSystemVersion {
149
+ func getFullVersion(separator: String = ".") -> String {
150
+ return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
151
+ }
152
+ }
153
+ extension Bundle {
154
+ var versionName: String? {
155
+ return infoDictionary?["CFBundleShortVersionString"] as? String
156
+ }
157
+ var versionCode: String? {
158
+ return infoDictionary?["CFBundleVersion"] as? String
159
+ }
160
+ }
161
+
162
+ extension ISO8601DateFormatter {
163
+ convenience init(_ formatOptions: Options) {
164
+ self.init()
165
+ self.formatOptions = formatOptions
166
+ }
167
+ }
168
+ extension Formatter {
169
+ static let iso8601withFractionalSeconds: ISO8601DateFormatter = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
170
+ }
171
+ extension Date {
172
+ var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
173
+ }
174
+ extension String {
175
+
176
+ var fileURL: URL {
177
+ return URL(fileURLWithPath: self)
178
+ }
179
+
180
+ var lastPathComponent: String {
181
+ get {
182
+ return fileURL.lastPathComponent
183
+ }
184
+ }
185
+ var iso8601withFractionalSeconds: Date? {
186
+ return Formatter.iso8601withFractionalSeconds.date(from: self)
187
+ }
188
+ func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
189
+ return trimmingCharacters(in: characterSet)
190
+ }
191
+ }
192
+
193
+ enum CustomError: Error {
194
+ // Throw when an unzip fail
195
+ case cannotUnzip
196
+ case cannotWrite
197
+ case cannotDecode
198
+ case cannotUnflat
199
+ case cannotCreateDirectory
200
+ case cannotDeleteDirectory
201
+ case cannotDecryptSessionKey
202
+ case invalidBase64
203
+
204
+ // Throw in all other cases
205
+ case unexpected(code: Int)
206
+ }
207
+
208
+ extension CustomError: LocalizedError {
209
+ public var errorDescription: String? {
210
+ switch self {
211
+ case .cannotUnzip:
212
+ return NSLocalizedString(
213
+ "The file cannot be unzip",
214
+ comment: "Invalid zip"
215
+ )
216
+ case .cannotCreateDirectory:
217
+ return NSLocalizedString(
218
+ "The folder cannot be created",
219
+ comment: "Invalid folder"
220
+ )
221
+ case .cannotDeleteDirectory:
222
+ return NSLocalizedString(
223
+ "The folder cannot be deleted",
224
+ comment: "Invalid folder"
225
+ )
226
+ case .cannotUnflat:
227
+ return NSLocalizedString(
228
+ "The file cannot be unflat",
229
+ comment: "Invalid folder"
230
+ )
231
+ case .unexpected:
232
+ return NSLocalizedString(
233
+ "An unexpected error occurred.",
234
+ comment: "Unexpected Error"
235
+ )
236
+ case .cannotDecode:
237
+ return NSLocalizedString(
238
+ "Decoding the zip failed with this key",
239
+ comment: "Invalid public key"
240
+ )
241
+ case .cannotWrite:
242
+ return NSLocalizedString(
243
+ "Cannot write to the destination",
244
+ comment: "Invalid destination"
245
+ )
246
+ case .cannotDecryptSessionKey:
247
+ return NSLocalizedString(
248
+ "Decrypting the session key failed",
249
+ comment: "Invalid session key"
250
+ )
251
+ case .invalidBase64:
252
+ return NSLocalizedString(
253
+ "Decrypting the base64 failed",
254
+ comment: "Invalid checksum key"
255
+ )
256
+ }
257
+ }
258
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "6.11.2",
3
+ "version": "6.12.1",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",
@@ -65,8 +65,8 @@
65
65
  "@capacitor/ios": "^6.2.0",
66
66
  "@ionic/eslint-config": "^0.4.0",
67
67
  "@ionic/prettier-config": "^4.0.0",
68
- "@ionic/swiftlint-config": "^1.1.2",
69
- "@types/node": "^22.10.1",
68
+ "@ionic/swiftlint-config": "^2.0.0",
69
+ "@types/node": "^22.10.5",
70
70
  "@typescript-eslint/eslint-plugin": "^7.18.0",
71
71
  "@typescript-eslint/parser": "^7.18.0",
72
72
  "eslint": "^8.57.1",
@@ -74,7 +74,7 @@
74
74
  "prettier": "^3.4.2",
75
75
  "prettier-plugin-java": "^2.6.5",
76
76
  "rimraf": "^6.0.1",
77
- "rollup": "^4.28.0",
77
+ "rollup": "^4.30.1",
78
78
  "swiftlint": "^2.0.0",
79
79
  "typescript": "^5.7.2"
80
80
  },