@capgo/capacitor-updater 8.0.0 → 8.1.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.
Files changed (64) hide show
  1. package/CapgoCapacitorUpdater.podspec +7 -5
  2. package/Package.swift +37 -0
  3. package/README.md +1461 -231
  4. package/android/build.gradle +29 -12
  5. package/android/proguard-rules.pro +45 -0
  6. package/android/src/main/AndroidManifest.xml +0 -1
  7. package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +223 -195
  8. package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +23 -23
  9. package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +13 -0
  10. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +2159 -1234
  11. package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +1507 -0
  12. package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +330 -121
  13. package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +28 -0
  14. package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +43 -49
  15. package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +4 -4
  16. package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +260 -0
  17. package/android/src/main/java/ee/forgr/capacitor_updater/DeviceIdHelper.java +221 -0
  18. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +808 -117
  19. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +156 -0
  20. package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +32 -0
  21. package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +338 -0
  22. package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +72 -0
  23. package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +169 -0
  24. package/dist/docs.json +2187 -625
  25. package/dist/esm/definitions.d.ts +1286 -249
  26. package/dist/esm/definitions.js.map +1 -1
  27. package/dist/esm/history.d.ts +1 -0
  28. package/dist/esm/history.js +283 -0
  29. package/dist/esm/history.js.map +1 -0
  30. package/dist/esm/index.d.ts +3 -2
  31. package/dist/esm/index.js +5 -4
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/web.d.ts +36 -41
  34. package/dist/esm/web.js +94 -35
  35. package/dist/esm/web.js.map +1 -1
  36. package/dist/plugin.cjs.js +376 -35
  37. package/dist/plugin.cjs.js.map +1 -1
  38. package/dist/plugin.js +376 -35
  39. package/dist/plugin.js.map +1 -1
  40. package/ios/Sources/CapacitorUpdaterPlugin/AES.swift +69 -0
  41. package/ios/Sources/CapacitorUpdaterPlugin/BigInt.swift +55 -0
  42. package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleInfo.swift +37 -10
  43. package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleStatus.swift +1 -1
  44. package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1605 -0
  45. package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +1526 -0
  46. package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +267 -0
  47. package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +220 -0
  48. package/ios/Sources/CapacitorUpdaterPlugin/DeviceIdHelper.swift +120 -0
  49. package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +311 -0
  50. package/ios/Sources/CapacitorUpdaterPlugin/Logger.swift +310 -0
  51. package/ios/Sources/CapacitorUpdaterPlugin/RSA.swift +274 -0
  52. package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +112 -0
  53. package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/UserDefaultsExtension.swift +0 -2
  54. package/package.json +41 -35
  55. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +0 -1130
  56. package/ios/Plugin/CapacitorUpdater.swift +0 -858
  57. package/ios/Plugin/CapacitorUpdaterPlugin.h +0 -10
  58. package/ios/Plugin/CapacitorUpdaterPlugin.m +0 -27
  59. package/ios/Plugin/CapacitorUpdaterPlugin.swift +0 -675
  60. package/ios/Plugin/CryptoCipher.swift +0 -240
  61. /package/{LICENCE → LICENSE} +0 -0
  62. /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayCondition.swift +0 -0
  63. /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayUntilNext.swift +0 -0
  64. /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/Info.plist +0 -0
@@ -12,142 +12,351 @@ package ee.forgr.capacitor_updater;
12
12
  * references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
13
13
  */
14
14
  import android.util.Base64;
15
+ import java.io.BufferedInputStream;
16
+ import java.io.DataInputStream;
17
+ import java.io.File;
18
+ import java.io.FileInputStream;
19
+ import java.io.FileOutputStream;
20
+ import java.io.IOException;
15
21
  import java.security.GeneralSecurityException;
16
22
  import java.security.InvalidAlgorithmParameterException;
17
23
  import java.security.InvalidKeyException;
18
24
  import java.security.KeyFactory;
25
+ import java.security.MessageDigest;
19
26
  import java.security.NoSuchAlgorithmException;
20
- import java.security.PrivateKey;
27
+ import java.security.PublicKey;
21
28
  import java.security.spec.InvalidKeySpecException;
22
- import java.security.spec.MGF1ParameterSpec;
23
- import java.security.spec.PKCS8EncodedKeySpec;
29
+ import java.security.spec.X509EncodedKeySpec;
24
30
  import javax.crypto.BadPaddingException;
25
31
  import javax.crypto.Cipher;
26
32
  import javax.crypto.IllegalBlockSizeException;
27
33
  import javax.crypto.NoSuchPaddingException;
28
34
  import javax.crypto.SecretKey;
29
35
  import javax.crypto.spec.IvParameterSpec;
30
- import javax.crypto.spec.OAEPParameterSpec;
31
- import javax.crypto.spec.PSource;
32
36
  import javax.crypto.spec.SecretKeySpec;
33
37
 
34
38
  public class CryptoCipher {
35
39
 
36
- public static byte[] decryptRSA(byte[] source, PrivateKey privateKey)
37
- throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
38
- Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
39
- OAEPParameterSpec oaepParams = new OAEPParameterSpec(
40
- "SHA-256",
41
- "MGF1",
42
- new MGF1ParameterSpec("SHA-256"),
43
- PSource.PSpecified.DEFAULT
44
- );
45
- cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
46
- byte[] decryptedBytes = cipher.doFinal(source);
47
- return decryptedBytes;
48
- }
49
-
50
- public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
51
- try {
52
- IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
53
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
54
- SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
55
- cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
56
- byte[] decryptedText = cipher.doFinal(cipherText);
57
- return decryptedText;
58
- } catch (Exception e) {
59
- e.printStackTrace();
60
- }
61
- return null;
62
- }
63
-
64
- public static SecretKey byteToSessionKey(byte[] sessionKey) {
65
- // rebuild key using SecretKeySpec
66
- SecretKey originalKey = new SecretKeySpec(
67
- sessionKey,
68
- 0,
69
- sessionKey.length,
70
- "AES"
71
- );
72
- return originalKey;
73
- }
74
-
75
- private static PrivateKey readPkcs8PrivateKey(byte[] pkcs8Bytes)
76
- throws GeneralSecurityException {
77
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
78
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Bytes);
79
- try {
80
- return keyFactory.generatePrivate(keySpec);
81
- } catch (InvalidKeySpecException e) {
82
- throw new IllegalArgumentException("Unexpected key format!", e);
83
- }
84
- }
85
-
86
- private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
87
- byte[] bytes = new byte[byteArray1.length + byteArray2.length];
88
- System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
89
- System.arraycopy(
90
- byteArray2,
91
- 0,
92
- bytes,
93
- byteArray1.length,
94
- byteArray2.length
95
- );
96
- return bytes;
97
- }
98
-
99
- private static PrivateKey readPkcs1PrivateKey(byte[] pkcs1Bytes)
100
- throws GeneralSecurityException {
101
- // We can't use Java internal APIs to parse ASN.1 structures, so we build a PKCS#8 key Java can understand
102
- int pkcs1Length = pkcs1Bytes.length;
103
- int totalLength = pkcs1Length + 22;
104
- byte[] pkcs8Header = new byte[] {
105
- 0x30,
106
- (byte) 0x82,
107
- (byte) ((totalLength >> 8) & 0xff),
108
- (byte) (totalLength & 0xff), // Sequence + total length
109
- 0x2,
110
- 0x1,
111
- 0x0, // Integer (0)
112
- 0x30,
113
- 0xD,
114
- 0x6,
115
- 0x9,
116
- 0x2A,
117
- (byte) 0x86,
118
- 0x48,
119
- (byte) 0x86,
120
- (byte) 0xF7,
121
- 0xD,
122
- 0x1,
123
- 0x1,
124
- 0x1,
125
- 0x5,
126
- 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
127
- 0x4,
128
- (byte) 0x82,
129
- (byte) ((pkcs1Length >> 8) & 0xff),
130
- (byte) (pkcs1Length & 0xff), // Octet string + length
40
+ private static Logger logger;
41
+
42
+ public static void setLogger(Logger loggerInstance) {
43
+ logger = loggerInstance;
44
+ }
45
+
46
+ public static byte[] decryptRSA(byte[] source, PublicKey publicKey)
47
+ throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
48
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
49
+ cipher.init(Cipher.DECRYPT_MODE, publicKey);
50
+ byte[] decryptedBytes = cipher.doFinal(source);
51
+ return decryptedBytes;
52
+ }
53
+
54
+ public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
55
+ try {
56
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
57
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
58
+ SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
59
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
60
+ return cipher.doFinal(cipherText);
61
+ } catch (Exception e) {
62
+ e.printStackTrace();
63
+ }
64
+ return null;
65
+ }
66
+
67
+ public static SecretKey byteToSessionKey(byte[] sessionKey) {
68
+ // rebuild key using SecretKeySpec
69
+ return new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
70
+ }
71
+
72
+ private static PublicKey readX509PublicKey(byte[] x509Bytes) throws GeneralSecurityException {
73
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
74
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509Bytes);
75
+ try {
76
+ return keyFactory.generatePublic(keySpec);
77
+ } catch (InvalidKeySpecException e) {
78
+ throw new IllegalArgumentException("Unexpected key format!", e);
79
+ }
80
+ }
81
+
82
+ public static PublicKey stringToPublicKey(String public_key) throws GeneralSecurityException {
83
+ String pkcs1Pem = public_key
84
+ .replaceAll("\\s+", "")
85
+ .replace("-----BEGINRSAPUBLICKEY-----", "")
86
+ .replace("-----ENDRSAPUBLICKEY-----", "");
87
+
88
+ byte[] pkcs1EncodedBytes = Base64.decode(pkcs1Pem, Base64.DEFAULT);
89
+ return readPkcs1PublicKey(pkcs1EncodedBytes);
90
+ }
91
+
92
+ // since the public key is in pkcs1 format, we have to convert it to x509 format similar
93
+ // to what needs done with the private key converting to pkcs8 format
94
+ // so, the rest of the code below here is adapted from here https://stackoverflow.com/a/54246646
95
+ private static final int SEQUENCE_TAG = 0x30;
96
+ private static final int BIT_STRING_TAG = 0x03;
97
+ private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
98
+ private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE = {
99
+ (byte) 0x30,
100
+ (byte) 0x0d,
101
+ (byte) 0x06,
102
+ (byte) 0x09,
103
+ (byte) 0x2a,
104
+ (byte) 0x86,
105
+ (byte) 0x48,
106
+ (byte) 0x86,
107
+ (byte) 0xf7,
108
+ (byte) 0x0d,
109
+ (byte) 0x01,
110
+ (byte) 0x01,
111
+ (byte) 0x01,
112
+ (byte) 0x05,
113
+ (byte) 0x00
131
114
  };
132
- byte[] pkcs8bytes = join(pkcs8Header, pkcs1Bytes);
133
- return readPkcs8PrivateKey(pkcs8bytes);
134
- }
135
-
136
- public static PrivateKey stringToPrivateKey(String private_key)
137
- throws GeneralSecurityException {
138
- // Base64 decode the result
139
-
140
- String pkcs1Pem = private_key.toString();
141
- pkcs1Pem = pkcs1Pem.replace("-----BEGIN RSA PRIVATE KEY-----", "");
142
- pkcs1Pem = pkcs1Pem.replace("-----END RSA PRIVATE KEY-----", "");
143
- pkcs1Pem = pkcs1Pem.replace("\\n", "");
144
- pkcs1Pem = pkcs1Pem.replace(" ", "");
145
-
146
- byte[] pkcs1EncodedBytes = Base64.decode(
147
- pkcs1Pem.getBytes(),
148
- Base64.DEFAULT
149
- );
150
- // extract the private key
151
- return readPkcs1PrivateKey(pkcs1EncodedBytes);
152
- }
115
+
116
+ private static PublicKey readPkcs1PublicKey(byte[] pkcs1Bytes)
117
+ throws NoSuchAlgorithmException, InvalidKeySpecException, GeneralSecurityException {
118
+ // convert the pkcs1 public key to an x509 favorable format
119
+ byte[] keyBitString = createDEREncoding(BIT_STRING_TAG, joinPublic(NO_UNUSED_BITS, pkcs1Bytes));
120
+ byte[] keyInfoValue = joinPublic(RSA_ALGORITHM_IDENTIFIER_SEQUENCE, keyBitString);
121
+ byte[] keyInfoSequence = createDEREncoding(SEQUENCE_TAG, keyInfoValue);
122
+ return readX509PublicKey(keyInfoSequence);
123
+ }
124
+
125
+ private static byte[] joinPublic(byte[]... bas) {
126
+ int len = 0;
127
+ for (int i = 0; i < bas.length; i++) {
128
+ len += bas[i].length;
129
+ }
130
+
131
+ byte[] buf = new byte[len];
132
+ int off = 0;
133
+ for (int i = 0; i < bas.length; i++) {
134
+ System.arraycopy(bas[i], 0, buf, off, bas[i].length);
135
+ off += bas[i].length;
136
+ }
137
+
138
+ return buf;
139
+ }
140
+
141
+ public static void decryptFile(final File file, final String publicKey, final String ivSessionKey) throws IOException {
142
+ if (publicKey.isEmpty() || ivSessionKey == null || ivSessionKey.isEmpty() || ivSessionKey.split(":").length != 2) {
143
+ logger.info("Encryption not set, no public key or session, ignored");
144
+ return;
145
+ }
146
+ if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
147
+ logger.error("The public key is not a valid RSA Public key");
148
+ return;
149
+ }
150
+
151
+ try {
152
+ String ivB64 = ivSessionKey.split(":")[0];
153
+ String sessionKeyB64 = ivSessionKey.split(":")[1];
154
+ byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
155
+ byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
156
+ PublicKey pKey = CryptoCipher.stringToPublicKey(publicKey);
157
+ byte[] decryptedSessionKey = CryptoCipher.decryptRSA(sessionKey, pKey);
158
+
159
+ SecretKey sKey = CryptoCipher.byteToSessionKey(decryptedSessionKey);
160
+ byte[] content = new byte[(int) file.length()];
161
+
162
+ try (
163
+ final FileInputStream fis = new FileInputStream(file);
164
+ final BufferedInputStream bis = new BufferedInputStream(fis);
165
+ final DataInputStream dis = new DataInputStream(bis)
166
+ ) {
167
+ dis.readFully(content);
168
+ dis.close();
169
+ byte[] decrypted = CryptoCipher.decryptAES(content, sKey, iv);
170
+ // write the decrypted string to the file
171
+ try (final FileOutputStream fos = new FileOutputStream(file.getAbsolutePath())) {
172
+ fos.write(decrypted);
173
+ }
174
+ }
175
+ } catch (GeneralSecurityException e) {
176
+ logger.info("decryptFile fail");
177
+ e.printStackTrace();
178
+ throw new IOException("GeneralSecurityException");
179
+ }
180
+ }
181
+
182
+ private static byte[] hexStringToByteArray(String s) {
183
+ int len = s.length();
184
+ byte[] data = new byte[len / 2];
185
+ for (int i = 0; i < len; i += 2) {
186
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
187
+ }
188
+ return data;
189
+ }
190
+
191
+ public static String decryptChecksum(String checksum, String publicKey) throws IOException {
192
+ if (publicKey.isEmpty()) {
193
+ logger.error("No encryption set (public key) ignored");
194
+ return checksum;
195
+ }
196
+ try {
197
+ // TODO: remove this in a month or two
198
+ // Determine if input is hex or base64 encoded
199
+ // Hex strings only contain 0-9 and a-f, while base64 contains other characters
200
+ byte[] checksumBytes;
201
+ String detectedFormat;
202
+ if (checksum.matches("^[0-9a-fA-F]+$")) {
203
+ // Hex encoded (new format from CLI for plugin versions >= 5.30.0, 6.30.0, 7.30.0)
204
+ checksumBytes = hexStringToByteArray(checksum);
205
+ detectedFormat = "hex";
206
+ } else {
207
+ // TODO: remove backwards compatibility
208
+ // Base64 encoded (old format for backwards compatibility)
209
+ checksumBytes = Base64.decode(checksum, Base64.DEFAULT);
210
+ detectedFormat = "base64";
211
+ }
212
+ logger.debug(
213
+ "Received encrypted checksum format: " +
214
+ detectedFormat +
215
+ " (length: " +
216
+ checksum.length() +
217
+ " chars, " +
218
+ checksumBytes.length +
219
+ " bytes)"
220
+ );
221
+ PublicKey pKey = CryptoCipher.stringToPublicKey(publicKey);
222
+ byte[] decryptedChecksum = CryptoCipher.decryptRSA(checksumBytes, pKey);
223
+ // Return as hex string to match calcChecksum output format
224
+ StringBuilder hexString = new StringBuilder();
225
+ for (byte b : decryptedChecksum) {
226
+ String hex = Integer.toHexString(0xff & b);
227
+ if (hex.length() == 1) hexString.append('0');
228
+ hexString.append(hex);
229
+ }
230
+ String result = hexString.toString();
231
+
232
+ // Detect checksum algorithm based on length
233
+ String detectedAlgorithm;
234
+ if (decryptedChecksum.length == 32) {
235
+ detectedAlgorithm = "SHA-256";
236
+ } else if (decryptedChecksum.length == 4) {
237
+ detectedAlgorithm = "CRC32 (deprecated)";
238
+ logger.error(
239
+ "CRC32 checksum detected. This algorithm is deprecated and no longer supported. Please update your CLI to use SHA-256 checksums."
240
+ );
241
+ } else {
242
+ detectedAlgorithm = "unknown (" + decryptedChecksum.length + " bytes)";
243
+ logger.error(
244
+ "Unknown checksum algorithm detected with " + decryptedChecksum.length + " bytes. Expected SHA-256 (32 bytes)."
245
+ );
246
+ }
247
+ logger.debug(
248
+ "Decrypted checksum: " +
249
+ detectedAlgorithm +
250
+ " hex format (length: " +
251
+ result.length() +
252
+ " chars, " +
253
+ decryptedChecksum.length +
254
+ " bytes)"
255
+ );
256
+ return result;
257
+ } catch (GeneralSecurityException e) {
258
+ logger.error("decryptChecksum fail: " + e.getMessage());
259
+ throw new IOException("Decryption failed: " + e.getMessage());
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Detect checksum algorithm based on hex string length.
265
+ * SHA-256 = 64 hex chars (32 bytes)
266
+ * CRC32 = 8 hex chars (4 bytes)
267
+ */
268
+ public static String detectChecksumAlgorithm(String hexChecksum) {
269
+ if (hexChecksum == null || hexChecksum.isEmpty()) {
270
+ return "empty";
271
+ }
272
+ int len = hexChecksum.length();
273
+ if (len == 64) {
274
+ return "SHA-256";
275
+ } else if (len == 8) {
276
+ return "CRC32 (deprecated)";
277
+ } else {
278
+ return "unknown (" + len + " hex chars)";
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Log checksum info and warn if deprecated algorithm detected.
284
+ */
285
+ public static void logChecksumInfo(String label, String hexChecksum) {
286
+ String algorithm = detectChecksumAlgorithm(hexChecksum);
287
+ logger.debug(label + ": " + algorithm + " hex format (length: " + hexChecksum.length() + " chars)");
288
+ if (algorithm.contains("CRC32")) {
289
+ logger.error(
290
+ "CRC32 checksum detected. This algorithm is deprecated and no longer supported. Please update your CLI to use SHA-256 checksums."
291
+ );
292
+ } else if (algorithm.contains("unknown")) {
293
+ logger.error(
294
+ "Unknown checksum algorithm detected. Expected SHA-256 (64 hex chars) but got " + hexChecksum.length() + " chars."
295
+ );
296
+ }
297
+ }
298
+
299
+ public static String calcChecksum(File file) {
300
+ final int BUFFER_SIZE = 1024 * 1024 * 5; // 5 MB buffer size
301
+ MessageDigest digest;
302
+ try {
303
+ digest = MessageDigest.getInstance("SHA-256");
304
+ } catch (java.security.NoSuchAlgorithmException e) {
305
+ logger.error("SHA-256 algorithm not available");
306
+ return "";
307
+ }
308
+
309
+ try (FileInputStream fis = new FileInputStream(file)) {
310
+ byte[] buffer = new byte[BUFFER_SIZE];
311
+ int length;
312
+ while ((length = fis.read(buffer)) != -1) {
313
+ digest.update(buffer, 0, length);
314
+ }
315
+ byte[] hash = digest.digest();
316
+ StringBuilder hexString = new StringBuilder();
317
+ for (byte b : hash) {
318
+ String hex = Integer.toHexString(0xff & b);
319
+ if (hex.length() == 1) hexString.append('0');
320
+ hexString.append(hex);
321
+ }
322
+ return hexString.toString();
323
+ } catch (IOException e) {
324
+ logger.error("Cannot calc checksum v2: " + file.getPath() + " " + e.getMessage());
325
+ return "";
326
+ }
327
+ }
328
+
329
+ private static byte[] createDEREncoding(int tag, byte[] value) {
330
+ if (tag < 0 || tag >= 0xFF) {
331
+ throw new IllegalArgumentException("Currently only single byte tags supported");
332
+ }
333
+
334
+ byte[] lengthEncoding = createDERLengthEncoding(value.length);
335
+
336
+ int size = 1 + lengthEncoding.length + value.length;
337
+ byte[] derEncodingBuf = new byte[size];
338
+
339
+ int off = 0;
340
+ derEncodingBuf[off++] = (byte) tag;
341
+ System.arraycopy(lengthEncoding, 0, derEncodingBuf, off, lengthEncoding.length);
342
+ off += lengthEncoding.length;
343
+ System.arraycopy(value, 0, derEncodingBuf, off, value.length);
344
+
345
+ return derEncodingBuf;
346
+ }
347
+
348
+ private static byte[] createDERLengthEncoding(int size) {
349
+ if (size <= 0x7F) {
350
+ // single byte length encoding
351
+ return new byte[] { (byte) size };
352
+ } else if (size <= 0xFF) {
353
+ // double byte length encoding
354
+ return new byte[] { (byte) 0x81, (byte) size };
355
+ } else if (size <= 0xFFFF) {
356
+ // triple byte length encoding
357
+ return new byte[] { (byte) 0x82, (byte) (size >> Byte.SIZE), (byte) size };
358
+ }
359
+
360
+ throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
361
+ }
153
362
  }
@@ -0,0 +1,28 @@
1
+ package ee.forgr.capacitor_updater;
2
+
3
+ import org.json.JSONArray;
4
+
5
+ public class DataManager {
6
+
7
+ private static DataManager instance;
8
+ private JSONArray currentManifest;
9
+
10
+ private DataManager() {}
11
+
12
+ public static synchronized DataManager getInstance() {
13
+ if (instance == null) {
14
+ instance = new DataManager();
15
+ }
16
+ return instance;
17
+ }
18
+
19
+ public void setManifest(JSONArray manifest) {
20
+ this.currentManifest = manifest;
21
+ }
22
+
23
+ public JSONArray getAndClearManifest() {
24
+ JSONArray manifest = this.currentManifest;
25
+ this.currentManifest = null;
26
+ return manifest;
27
+ }
28
+ }
@@ -6,57 +6,51 @@
6
6
 
7
7
  package ee.forgr.capacitor_updater;
8
8
 
9
- import com.google.gson.annotations.SerializedName;
9
+ import androidx.annotation.NonNull;
10
10
  import java.util.Objects;
11
11
 
12
12
  public class DelayCondition {
13
13
 
14
- @SerializedName("kind")
15
- private DelayUntilNext kind;
16
-
17
- @SerializedName("value")
18
- private String value;
19
-
20
- public DelayCondition(DelayUntilNext kind, String value) {
21
- this.kind = kind;
22
- this.value = value;
23
- }
24
-
25
- public DelayUntilNext getKind() {
26
- return kind;
27
- }
28
-
29
- public void setKind(DelayUntilNext kind) {
30
- this.kind = kind;
31
- }
32
-
33
- public String getValue() {
34
- return value;
35
- }
36
-
37
- public void setValue(String value) {
38
- this.value = value;
39
- }
40
-
41
- @Override
42
- public boolean equals(Object o) {
43
- if (this == o) return true;
44
- if (!(o instanceof DelayCondition)) return false;
45
- DelayCondition that = (DelayCondition) o;
46
- return (
47
- getKind() == that.getKind() && Objects.equals(getValue(), that.getValue())
48
- );
49
- }
50
-
51
- @Override
52
- public int hashCode() {
53
- return Objects.hash(getKind(), getValue());
54
- }
55
-
56
- @Override
57
- public String toString() {
58
- return (
59
- "DelayCondition{" + "kind=" + kind + ", value='" + value + '\'' + '}'
60
- );
61
- }
14
+ private DelayUntilNext kind;
15
+
16
+ private String value;
17
+
18
+ public DelayCondition(DelayUntilNext kind, String value) {
19
+ this.kind = kind;
20
+ this.value = value;
21
+ }
22
+
23
+ public DelayUntilNext getKind() {
24
+ return kind;
25
+ }
26
+
27
+ public void setKind(DelayUntilNext kind) {
28
+ this.kind = kind;
29
+ }
30
+
31
+ public String getValue() {
32
+ return value;
33
+ }
34
+
35
+ public void setValue(String value) {
36
+ this.value = value;
37
+ }
38
+
39
+ @Override
40
+ public boolean equals(Object o) {
41
+ if (this == o) return true;
42
+ if (!(o instanceof DelayCondition that)) return false;
43
+ return (getKind() == that.getKind() && Objects.equals(getValue(), that.getValue()));
44
+ }
45
+
46
+ @Override
47
+ public int hashCode() {
48
+ return Objects.hash(getKind(), getValue());
49
+ }
50
+
51
+ @NonNull
52
+ @Override
53
+ public String toString() {
54
+ return ("DelayCondition{" + "kind=" + kind + ", value='" + value + '\'' + '}');
55
+ }
62
56
  }
@@ -7,8 +7,8 @@
7
7
  package ee.forgr.capacitor_updater;
8
8
 
9
9
  public enum DelayUntilNext {
10
- background,
11
- kill,
12
- nativeVersion,
13
- date,
10
+ background,
11
+ kill,
12
+ nativeVersion,
13
+ date
14
14
  }