@capgo/capacitor-updater 6.30.0 → 6.35.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.
@@ -160,6 +160,8 @@ struct AppVersionDec: Decodable {
160
160
  let breaking: Bool?
161
161
  let data: [String: String]?
162
162
  let manifest: [ManifestEntry]?
163
+ let link: String?
164
+ let comment: String?
163
165
  // The HTTP status code is captured separately in CapgoUpdater; this struct only mirrors JSON.
164
166
  }
165
167
 
@@ -174,6 +176,8 @@ public class AppVersion: NSObject {
174
176
  var breaking: Bool?
175
177
  var data: [String: String]?
176
178
  var manifest: [ManifestEntry]?
179
+ var link: String?
180
+ var comment: String?
177
181
  var statusCode: Int = 0
178
182
  }
179
183
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "6.30.0",
3
+ "version": "6.35.0",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",
@@ -37,6 +37,7 @@
37
37
  "ionic",
38
38
  "appflow alternative",
39
39
  "capawesome alternative",
40
+ "@capawesome/capacitor-live-update",
40
41
  "native"
41
42
  ],
42
43
  "scripts": {
@@ -1,222 +0,0 @@
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
- package ee.forgr.capacitor_updater;
8
-
9
- /**
10
- * Created by Awesometic
11
- * It's encrypt returns Base64 encoded, and also decrypt for Base64 encoded cipher
12
- * references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
13
- *
14
- * V1 Encryption - uses privateKey (deprecated but kept for backwards compatibility)
15
- */
16
- import android.util.Base64;
17
- import android.util.Log;
18
- import java.io.BufferedInputStream;
19
- import java.io.DataInputStream;
20
- import java.io.File;
21
- import java.io.FileInputStream;
22
- import java.io.FileOutputStream;
23
- import java.io.IOException;
24
- import java.security.GeneralSecurityException;
25
- import java.security.InvalidAlgorithmParameterException;
26
- import java.security.InvalidKeyException;
27
- import java.security.KeyFactory;
28
- import java.security.NoSuchAlgorithmException;
29
- import java.security.PrivateKey;
30
- import java.security.PublicKey;
31
- import java.security.spec.InvalidKeySpecException;
32
- import java.security.spec.MGF1ParameterSpec;
33
- import java.security.spec.PKCS8EncodedKeySpec;
34
- import java.security.spec.X509EncodedKeySpec;
35
- import java.util.zip.CRC32;
36
- import javax.crypto.BadPaddingException;
37
- import javax.crypto.Cipher;
38
- import javax.crypto.IllegalBlockSizeException;
39
- import javax.crypto.NoSuchPaddingException;
40
- import javax.crypto.SecretKey;
41
- import javax.crypto.spec.IvParameterSpec;
42
- import javax.crypto.spec.OAEPParameterSpec;
43
- import javax.crypto.spec.PSource;
44
- import javax.crypto.spec.SecretKeySpec;
45
-
46
- public class CryptoCipherV1 {
47
-
48
- private static Logger logger;
49
-
50
- public static void setLogger(Logger loggerInstance) {
51
- logger = loggerInstance;
52
- }
53
-
54
- public static byte[] decryptRSA(byte[] source, PrivateKey privateKey)
55
- throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
56
- Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
57
- OAEPParameterSpec oaepParams = new OAEPParameterSpec(
58
- "SHA-256",
59
- "MGF1",
60
- new MGF1ParameterSpec("SHA-256"),
61
- PSource.PSpecified.DEFAULT
62
- );
63
- cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
64
- return cipher.doFinal(source);
65
- }
66
-
67
- public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
68
- try {
69
- IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
70
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
71
- SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
72
- cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
73
- return cipher.doFinal(cipherText);
74
- } catch (Exception e) {
75
- e.printStackTrace();
76
- }
77
- return null;
78
- }
79
-
80
- public static SecretKey byteToSessionKey(byte[] sessionKey) {
81
- // rebuild key using SecretKeySpec
82
- return new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
83
- }
84
-
85
- private static PrivateKey readPkcs8PrivateKey(byte[] pkcs8Bytes) throws GeneralSecurityException {
86
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
87
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Bytes);
88
- try {
89
- return keyFactory.generatePrivate(keySpec);
90
- } catch (InvalidKeySpecException e) {
91
- throw new IllegalArgumentException("Unexpected key format!", e);
92
- }
93
- }
94
-
95
- private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
96
- byte[] bytes = new byte[byteArray1.length + byteArray2.length];
97
- System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
98
- System.arraycopy(byteArray2, 0, bytes, byteArray1.length, byteArray2.length);
99
- return bytes;
100
- }
101
-
102
- private static PrivateKey readPkcs1PrivateKey(byte[] pkcs1Bytes) throws GeneralSecurityException {
103
- // We can't use Java internal APIs to parse ASN.1 structures, so we build a PKCS#8 key Java can understand
104
- int pkcs1Length = pkcs1Bytes.length;
105
- int totalLength = pkcs1Length + 22;
106
- byte[] pkcs8Header = new byte[] {
107
- 0x30,
108
- (byte) 0x82,
109
- (byte) ((totalLength >> 8) & 0xff),
110
- (byte) (totalLength & 0xff), // Sequence + total length
111
- 0x2,
112
- 0x1,
113
- 0x0, // Integer (0)
114
- 0x30,
115
- 0xD,
116
- 0x6,
117
- 0x9,
118
- 0x2A,
119
- (byte) 0x86,
120
- 0x48,
121
- (byte) 0x86,
122
- (byte) 0xF7,
123
- 0xD,
124
- 0x1,
125
- 0x1,
126
- 0x1,
127
- 0x5,
128
- 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
129
- 0x4,
130
- (byte) 0x82,
131
- (byte) ((pkcs1Length >> 8) & 0xff),
132
- (byte) (pkcs1Length & 0xff) // Octet string + length
133
- };
134
- byte[] pkcs8bytes = join(pkcs8Header, pkcs1Bytes);
135
- return readPkcs8PrivateKey(pkcs8bytes);
136
- }
137
-
138
- public static PrivateKey stringToPrivateKey(String private_key) throws GeneralSecurityException {
139
- // Base64 decode the result
140
-
141
- String pkcs1Pem = private_key;
142
- pkcs1Pem = pkcs1Pem.replace("-----BEGIN RSA PRIVATE KEY-----", "");
143
- pkcs1Pem = pkcs1Pem.replace("-----END RSA PRIVATE KEY-----", "");
144
- pkcs1Pem = pkcs1Pem.replace("\\n", "");
145
- pkcs1Pem = pkcs1Pem.replace(" ", "");
146
-
147
- byte[] pkcs1EncodedBytes = Base64.decode(pkcs1Pem.getBytes(), Base64.DEFAULT);
148
- // extract the private key
149
- return readPkcs1PrivateKey(pkcs1EncodedBytes);
150
- }
151
-
152
- public static void decryptFile(final File file, final String privateKey, final String ivSessionKey, final String version)
153
- throws IOException {
154
- // (str != null && !str.isEmpty())
155
- if (privateKey == null || privateKey.isEmpty()) {
156
- Log.i("[Capacitor-updater]", "Cannot found privateKey");
157
- return;
158
- } else if (ivSessionKey == null || ivSessionKey.isEmpty() || ivSessionKey.split(":").length != 2) {
159
- Log.i("[Capacitor-updater]", "Cannot found sessionKey");
160
- return;
161
- }
162
- try {
163
- String ivB64 = ivSessionKey.split(":")[0];
164
- String sessionKeyB64 = ivSessionKey.split(":")[1];
165
- byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
166
- byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
167
- PrivateKey pKey = CryptoCipherV1.stringToPrivateKey(privateKey);
168
- byte[] decryptedSessionKey = CryptoCipherV1.decryptRSA(sessionKey, pKey);
169
- SecretKey sKey = CryptoCipherV1.byteToSessionKey(decryptedSessionKey);
170
- byte[] content = new byte[(int) file.length()];
171
-
172
- try (
173
- final FileInputStream fis = new FileInputStream(file);
174
- final BufferedInputStream bis = new BufferedInputStream(fis);
175
- final DataInputStream dis = new DataInputStream(bis)
176
- ) {
177
- dis.readFully(content);
178
- dis.close();
179
- byte[] decrypted = CryptoCipherV1.decryptAES(content, sKey, iv);
180
- // write the decrypted string to the file
181
- try (final FileOutputStream fos = new FileOutputStream(file.getAbsolutePath())) {
182
- fos.write(decrypted);
183
- }
184
- }
185
- } catch (GeneralSecurityException e) {
186
- Log.i("[Capacitor-updater]", "decryptFile fail");
187
- e.printStackTrace();
188
- throw new IOException("GeneralSecurityException");
189
- }
190
- }
191
-
192
- public static String calcChecksum(File file) {
193
- final int BUFFER_SIZE = 1024 * 1024 * 5; // 5 MB buffer size
194
- CRC32 crc = new CRC32();
195
-
196
- try (FileInputStream fis = new FileInputStream(file)) {
197
- byte[] buffer = new byte[BUFFER_SIZE];
198
- int length;
199
- while ((length = fis.read(buffer)) != -1) {
200
- crc.update(buffer, 0, length);
201
- }
202
- return String.format("%08x", crc.getValue());
203
- } catch (IOException e) {
204
- System.err.println("[Capacitor-updater]" + " Cannot calc checksum: " + file.getPath() + " " + e.getMessage());
205
- return "";
206
- }
207
- }
208
-
209
- public static PublicKey stringToPublicKey(String publicKey) {
210
- byte[] encoded = Base64.decode(publicKey, Base64.DEFAULT);
211
-
212
- KeyFactory keyFactory = null;
213
- try {
214
- keyFactory = KeyFactory.getInstance("RSA");
215
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
216
- return keyFactory.generatePublic(keySpec);
217
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
218
- Log.i("Capacitor-updater", "stringToPublicKey fail\nError:\n" + e.toString());
219
- return null;
220
- }
221
- }
222
- }
@@ -1,245 +0,0 @@
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
- import CommonCrypto
9
- import zlib
10
-
11
- ///
12
- /// Constants
13
- ///
14
- private enum CryptoCipherConstants {
15
- static let rsaKeySizeInBits: NSNumber = 2048
16
- static let aesAlgorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
17
- static let aesOptions: CCOptions = CCOptions(kCCOptionPKCS7Padding)
18
- static let rsaAlgorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
19
- }
20
- ///
21
- /// The RSA keypair. Includes both private and public key.
22
- ///
23
- public struct RSAKeyPair {
24
- private let privateKey: SecKey
25
- private let publicKey: SecKey
26
-
27
- #if DEBUG
28
- public var __debug_privateKey: SecKey { self.privateKey }
29
- public var __debug_publicKey: SecKey { self.publicKey }
30
- #endif
31
-
32
- fileprivate init(privateKey: SecKey, publicKey: SecKey) {
33
- self.privateKey = privateKey
34
- self.publicKey = publicKey
35
- }
36
-
37
- ///
38
- /// Takes the data and uses the private key to decrypt it.
39
- /// Returns the decrypted data.
40
- ///
41
- public func decrypt(data: Data) -> Data? {
42
- var error: Unmanaged<CFError>?
43
- if let decryptedData: CFData = SecKeyCreateDecryptedData(self.privateKey, CryptoCipherConstants.rsaAlgorithm, data as CFData, &error) {
44
- if error != nil {
45
- return nil
46
- } else {
47
- return decryptedData as Data
48
- }
49
- } else {
50
- return nil
51
- }
52
- }
53
-
54
- ///
55
- /// Takes the data and uses the public key to encrypt it.
56
- /// Returns the encrypted data.
57
- ///
58
- public func encrypt(data: Data) -> Data? {
59
- var error: Unmanaged<CFError>?
60
- if let encryptedData: CFData = SecKeyCreateEncryptedData(self.publicKey, CryptoCipherConstants.rsaAlgorithm, data as CFData, &error) {
61
- if error != nil {
62
- return nil
63
- } else {
64
- return encryptedData as Data
65
- }
66
- } else {
67
- return nil
68
- }
69
- }
70
-
71
- }
72
- ///
73
- /// The RSA public key.
74
- ///
75
- public struct RSAPrivateKey {
76
- private let privateKey: SecKey
77
-
78
- #if DEBUG
79
- public var __debug_privateKey: SecKey { self.privateKey }
80
- #endif
81
-
82
- fileprivate init(privateKey: SecKey) {
83
- self.privateKey = privateKey
84
- }
85
- ///
86
- /// Takes the data and uses the private key to decrypt it.
87
- /// Returns the decrypted data.
88
- ///
89
- public func decrypt(data: Data) -> Data? {
90
- var error: Unmanaged<CFError>?
91
- if let decryptedData: CFData = SecKeyCreateDecryptedData(self.privateKey, CryptoCipherConstants.rsaAlgorithm, data as CFData, &error) {
92
- if error != nil {
93
- return nil
94
- } else {
95
- return decryptedData as Data
96
- }
97
- } else {
98
- return nil
99
- }
100
- }
101
-
102
- ///
103
- /// Allows you to export the RSA public key to a format (so you can send over the net).
104
- ///
105
- public func export() -> Data? {
106
- return privateKey.exportToData()
107
- }
108
-
109
- ///
110
- /// Allows you to load an RSA public key (i.e. one downloaded from the net).
111
- ///
112
- public static func load(rsaPrivateKey: String) -> RSAPrivateKey? {
113
- var privKey: String = rsaPrivateKey
114
- privKey = privKey.replacingOccurrences(of: "-----BEGIN RSA PRIVATE KEY-----", with: "")
115
- privKey = privKey.replacingOccurrences(of: "-----END RSA PRIVATE KEY-----", with: "")
116
- privKey = privKey.replacingOccurrences(of: "\\n+", with: "", options: .regularExpression)
117
- privKey = privKey.trimmingCharacters(in: .whitespacesAndNewlines)
118
- do {
119
- guard let rsaPrivateKeyData: Data = Data(base64Encoded: privKey) else {
120
- throw CustomError.cannotDecode
121
- }
122
- guard let privateKey: SecKey = .loadPrivateFromData(rsaPrivateKeyData) else {
123
- throw CustomError.cannotDecode
124
- }
125
- return RSAPrivateKey(privateKey: privateKey)
126
- } catch {
127
- print("Error load RSA: \(error)")
128
- return nil
129
- }
130
- }
131
- }
132
-
133
- fileprivate extension SecKey {
134
- func exportToData() -> Data? {
135
- var error: Unmanaged<CFError>?
136
- if let cfData: CFData = SecKeyCopyExternalRepresentation(self, &error) {
137
- if error != nil {
138
- return nil
139
- } else {
140
- return cfData as Data
141
- }
142
- } else {
143
- return nil
144
- }
145
- }
146
- static func loadPublicFromData(_ data: Data) -> SecKey? {
147
- let keyDict: [NSObject: NSObject] = [
148
- kSecAttrKeyType: kSecAttrKeyTypeRSA,
149
- kSecAttrKeyClass: kSecAttrKeyClassPublic,
150
- kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
151
- ]
152
- return SecKeyCreateWithData(data as NSData, keyDict as CFDictionary, nil)
153
- }
154
- static func loadPrivateFromData(_ data: Data) -> SecKey? {
155
- let keyDict: [NSObject: NSObject] = [
156
- kSecAttrKeyType: kSecAttrKeyTypeRSA,
157
- kSecAttrKeyClass: kSecAttrKeyClassPrivate,
158
- kSecAttrKeySizeInBits: CryptoCipherConstants.rsaKeySizeInBits
159
- ]
160
- return SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, nil)
161
- }
162
- }
163
-
164
- // V1 Encryption - uses privateKey (deprecated but kept for backwards compatibility)
165
- public struct CryptoCipherV1 {
166
- private static var logger: Logger?
167
-
168
- public static func setLogger(_ newLogger: Logger) {
169
- logger = newLogger
170
- }
171
-
172
- public static func calcChecksum(filePath: URL) -> String {
173
- let bufferSize = 1024 * 1024 * 5 // 5 MB
174
- var checksum = uLong(0)
175
-
176
- do {
177
- let fileHandle = try FileHandle(forReadingFrom: filePath)
178
- defer {
179
- fileHandle.closeFile()
180
- }
181
-
182
- while autoreleasepool(invoking: {
183
- let fileData = fileHandle.readData(ofLength: bufferSize)
184
- if fileData.count > 0 {
185
- checksum = fileData.withUnsafeBytes {
186
- crc32(checksum, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count))
187
- }
188
- return true // Continue
189
- } else {
190
- return false // End of file
191
- }
192
- }) {}
193
-
194
- return String(format: "%08X", checksum).lowercased()
195
- } catch {
196
- print("\("[Capacitor-updater]") Cannot get checksum: \(filePath.path)", error)
197
- return ""
198
- }
199
- }
200
- public static func decryptFile(filePath: URL, privateKey: String, sessionKey: String, version: String) throws {
201
- if privateKey.isEmpty {
202
- print("\("[Capacitor-updater]") Cannot found privateKey")
203
- return
204
- } else if sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
205
- print("\("[Capacitor-updater]") Cannot found sessionKey")
206
- return
207
- }
208
- do {
209
- guard let rsaPrivateKey: RSAPrivateKey = .load(rsaPrivateKey: privateKey) else {
210
- print("cannot decode privateKey", privateKey)
211
- throw CustomError.cannotDecode
212
- }
213
-
214
- let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
215
- guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
216
- print("cannot decode sessionKey", sessionKey)
217
- throw CustomError.cannotDecode
218
- }
219
-
220
- guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
221
- throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
222
- }
223
-
224
- guard let sessionKeyDataDecrypted = rsaPrivateKey.decrypt(data: sessionKeyDataEncrypted) else {
225
- throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
226
- }
227
-
228
- let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted, logger: logger ?? Logger(withTag: "[Capacitor-updater]"))
229
-
230
- guard let encryptedData = try? Data(contentsOf: filePath) else {
231
- throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
232
- }
233
-
234
- guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
235
- throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
236
- }
237
-
238
- try decryptedData.write(to: filePath)
239
-
240
- } catch {
241
- print("\("[Capacitor-updater]") Cannot decode: \(filePath.path)", error)
242
- throw CustomError.cannotDecode
243
- }
244
- }
245
- }