@capgo/capacitor-updater 6.1.17 → 6.1.19
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 +1 -5
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +130 -82
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +11 -30
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +4 -9
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +2 -15
- package/dist/docs.json +0 -32
- package/dist/esm/definitions.d.ts +0 -15
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/CapacitorUpdater.swift +222 -173
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +23 -32
- package/ios/Plugin/CryptoCipher.swift +1 -36
- package/ios/Plugin/CryptoCipherV2.swift +4 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -218,7 +218,6 @@ CapacitorUpdater can be configured with these options:
|
|
|
218
218
|
| **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 |
|
|
219
219
|
| **`allowModifyUrl`** | <code>boolean</code> | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | <code>false</code> | 5.4.0 |
|
|
220
220
|
| **`defaultChannel`** | <code>string</code> | Set the default channel for the app in the config. | <code>undefined</code> | 5.5.0 |
|
|
221
|
-
| **`signKey`** | <code>string</code> | Public key used for bundle signing. | <code>undefined</code> | 6.1.0 |
|
|
222
221
|
|
|
223
222
|
### Examples
|
|
224
223
|
|
|
@@ -246,8 +245,7 @@ In `capacitor.config.json`:
|
|
|
246
245
|
"localSupa": undefined,
|
|
247
246
|
"localSupaAnon": undefined,
|
|
248
247
|
"allowModifyUrl": undefined,
|
|
249
|
-
"defaultChannel": undefined
|
|
250
|
-
"signKey": undefined
|
|
248
|
+
"defaultChannel": undefined
|
|
251
249
|
}
|
|
252
250
|
}
|
|
253
251
|
}
|
|
@@ -282,7 +280,6 @@ const config: CapacitorConfig = {
|
|
|
282
280
|
localSupaAnon: undefined,
|
|
283
281
|
allowModifyUrl: undefined,
|
|
284
282
|
defaultChannel: undefined,
|
|
285
|
-
signKey: undefined,
|
|
286
283
|
},
|
|
287
284
|
},
|
|
288
285
|
};
|
|
@@ -937,7 +934,6 @@ Listen for app ready event in the App, let you know when app is ready to use
|
|
|
937
934
|
| **`version`** | <code>string</code> | The version code/name of this bundle/version | | |
|
|
938
935
|
| **`sessionKey`** | <code>string</code> | The session key for the update | <code>undefined</code> | 4.0.0 |
|
|
939
936
|
| **`checksum`** | <code>string</code> | The checksum for the update | <code>undefined</code> | 4.0.0 |
|
|
940
|
-
| **`signature`** | <code>string</code> | The signature of the update. Can be generated using capgo CLI | <code>undefined</code> | 6.1.0 |
|
|
941
937
|
|
|
942
938
|
|
|
943
939
|
#### BundleId
|
|
@@ -40,13 +40,11 @@ import java.io.InputStream;
|
|
|
40
40
|
import java.io.UnsupportedEncodingException;
|
|
41
41
|
import java.net.URL;
|
|
42
42
|
import java.net.URLConnection;
|
|
43
|
-
import java.nio.charset.StandardCharsets;
|
|
44
43
|
import java.security.GeneralSecurityException;
|
|
45
44
|
import java.security.MessageDigest;
|
|
46
45
|
import java.security.PrivateKey;
|
|
47
46
|
import java.security.PublicKey;
|
|
48
47
|
import java.security.SecureRandom;
|
|
49
|
-
import java.security.Signature;
|
|
50
48
|
import java.util.ArrayList;
|
|
51
49
|
import java.util.Date;
|
|
52
50
|
import java.util.Iterator;
|
|
@@ -55,7 +53,6 @@ import java.util.Objects;
|
|
|
55
53
|
import java.util.zip.CRC32;
|
|
56
54
|
import java.util.zip.ZipEntry;
|
|
57
55
|
import java.util.zip.ZipInputStream;
|
|
58
|
-
import javax.crypto.Cipher;
|
|
59
56
|
import javax.crypto.SecretKey;
|
|
60
57
|
import org.json.JSONException;
|
|
61
58
|
import org.json.JSONObject;
|
|
@@ -92,9 +89,10 @@ public class CapacitorUpdater {
|
|
|
92
89
|
public String defaultChannel = "";
|
|
93
90
|
public String appId = "";
|
|
94
91
|
public String privateKey = "";
|
|
92
|
+
public String publicKey = "";
|
|
93
|
+
public boolean hasOldPrivateKeyPropertyInConfig = false;
|
|
95
94
|
public String deviceID = "";
|
|
96
95
|
public int timeout = 20000;
|
|
97
|
-
public PublicKey signKey = null;
|
|
98
96
|
|
|
99
97
|
private final FilenameFilter filter = (f, name) -> {
|
|
100
98
|
// ignore directories generated by mac os x
|
|
@@ -285,7 +283,6 @@ public class CapacitorUpdater {
|
|
|
285
283
|
String sessionKey = bundle.getString(DownloadService.SESSIONKEY);
|
|
286
284
|
String checksum = bundle.getString(DownloadService.CHECKSUM);
|
|
287
285
|
String error = bundle.getString(DownloadService.ERROR);
|
|
288
|
-
String signature = bundle.getString(DownloadService.SIGNATURE);
|
|
289
286
|
Log.i(
|
|
290
287
|
CapacitorUpdater.TAG,
|
|
291
288
|
"res " +
|
|
@@ -299,8 +296,6 @@ public class CapacitorUpdater {
|
|
|
299
296
|
" " +
|
|
300
297
|
checksum +
|
|
301
298
|
" " +
|
|
302
|
-
signature +
|
|
303
|
-
" " +
|
|
304
299
|
error
|
|
305
300
|
);
|
|
306
301
|
if (dest == null) {
|
|
@@ -323,7 +318,6 @@ public class CapacitorUpdater {
|
|
|
323
318
|
version,
|
|
324
319
|
sessionKey,
|
|
325
320
|
checksum,
|
|
326
|
-
signature,
|
|
327
321
|
true
|
|
328
322
|
);
|
|
329
323
|
} else {
|
|
@@ -333,13 +327,32 @@ public class CapacitorUpdater {
|
|
|
333
327
|
}
|
|
334
328
|
};
|
|
335
329
|
|
|
330
|
+
private String decryptChecksum(String checksum, String version)
|
|
331
|
+
throws IOException {
|
|
332
|
+
if (this.publicKey.isEmpty()) {
|
|
333
|
+
Log.e(CapacitorUpdater.TAG, "The public key is empty");
|
|
334
|
+
return checksum;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
byte[] checksumBytes = Base64.decode(checksum, Base64.DEFAULT);
|
|
338
|
+
PublicKey pKey = CryptoCipherV2.stringToPublicKey(this.publicKey);
|
|
339
|
+
byte[] decryptedChecksum = CryptoCipherV2.decryptRSA(checksumBytes, pKey);
|
|
340
|
+
// return Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
341
|
+
String result = Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
342
|
+
return result.replaceAll("\\s", ""); // Remove all whitespace, including newlines
|
|
343
|
+
} catch (GeneralSecurityException e) {
|
|
344
|
+
Log.e(TAG, "decryptChecksum fail: " + e.getMessage());
|
|
345
|
+
this.sendStats("decrypt_fail", version);
|
|
346
|
+
throw new IOException("Decryption failed: " + e.getMessage());
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
336
350
|
public Boolean finishDownload(
|
|
337
351
|
String id,
|
|
338
352
|
String dest,
|
|
339
353
|
String version,
|
|
340
354
|
String sessionKey,
|
|
341
355
|
String checksumRes,
|
|
342
|
-
String signature,
|
|
343
356
|
Boolean setNext
|
|
344
357
|
) {
|
|
345
358
|
File downloaded = null;
|
|
@@ -349,36 +362,27 @@ public class CapacitorUpdater {
|
|
|
349
362
|
this.notifyDownload(id, 71);
|
|
350
363
|
downloaded = new File(this.documentsDir, dest);
|
|
351
364
|
|
|
352
|
-
|
|
353
|
-
if (!
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
this.sendStats("invalid_signature", version);
|
|
360
|
-
throw new GeneralSecurityException(
|
|
361
|
-
"Signature is not valid, cannot accept update"
|
|
362
|
-
);
|
|
365
|
+
String checksumDecrypted = Objects.requireNonNullElse(checksumRes, "");
|
|
366
|
+
if (!this.hasOldPrivateKeyPropertyInConfig && !sessionKey.isEmpty()) {
|
|
367
|
+
this.decryptFileV2(downloaded, sessionKey, version);
|
|
368
|
+
checksumDecrypted = this.decryptChecksum(checksumRes, version);
|
|
369
|
+
checksum = this.calcChecksumV2(downloaded);
|
|
363
370
|
} else {
|
|
364
|
-
|
|
371
|
+
this.decryptFile(downloaded, sessionKey, version);
|
|
372
|
+
checksum = this.calcChecksum(downloaded);
|
|
365
373
|
}
|
|
366
|
-
|
|
367
|
-
this.decryptFile(downloaded, sessionKey, version);
|
|
368
|
-
checksum = this.calcChecksum(downloaded);
|
|
369
374
|
if (
|
|
370
|
-
|
|
371
|
-
!
|
|
372
|
-
!checksumRes.equals(checksum)
|
|
375
|
+
(!checksumDecrypted.isEmpty() || !this.publicKey.isEmpty()) &&
|
|
376
|
+
!checksumDecrypted.equals(checksum)
|
|
373
377
|
) {
|
|
374
378
|
Log.e(
|
|
375
379
|
CapacitorUpdater.TAG,
|
|
376
|
-
"Error checksum " +
|
|
380
|
+
"Error checksum '" + checksumDecrypted + "' '" + checksum + "' '"
|
|
377
381
|
);
|
|
378
382
|
this.sendStats("checksum_fail");
|
|
379
383
|
throw new IOException("Checksum failed: " + id);
|
|
380
384
|
}
|
|
381
|
-
} catch (IOException
|
|
385
|
+
} catch (IOException e) {
|
|
382
386
|
final Boolean res = this.delete(id);
|
|
383
387
|
if (!res) {
|
|
384
388
|
Log.i(CapacitorUpdater.TAG, "Double error, cannot cleanup: " + version);
|
|
@@ -443,8 +447,7 @@ public class CapacitorUpdater {
|
|
|
443
447
|
final String version,
|
|
444
448
|
final String sessionKey,
|
|
445
449
|
final String checksum,
|
|
446
|
-
final String dest
|
|
447
|
-
final String signature
|
|
450
|
+
final String dest
|
|
448
451
|
) {
|
|
449
452
|
Intent intent = new Intent(this.activity, DownloadService.class);
|
|
450
453
|
intent.putExtra(DownloadService.URL, url);
|
|
@@ -457,7 +460,6 @@ public class CapacitorUpdater {
|
|
|
457
460
|
intent.putExtra(DownloadService.VERSION, version);
|
|
458
461
|
intent.putExtra(DownloadService.SESSIONKEY, sessionKey);
|
|
459
462
|
intent.putExtra(DownloadService.CHECKSUM, checksum);
|
|
460
|
-
intent.putExtra(DownloadService.SIGNATURE, signature);
|
|
461
463
|
this.activity.startService(intent);
|
|
462
464
|
}
|
|
463
465
|
|
|
@@ -544,44 +546,100 @@ public class CapacitorUpdater {
|
|
|
544
546
|
}
|
|
545
547
|
}
|
|
546
548
|
|
|
547
|
-
private
|
|
548
|
-
final
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return
|
|
549
|
+
private String calcChecksumV2(File file) {
|
|
550
|
+
final int BUFFER_SIZE = 1024 * 1024 * 5; // 5 MB buffer size
|
|
551
|
+
MessageDigest digest;
|
|
552
|
+
try {
|
|
553
|
+
digest = MessageDigest.getInstance("SHA-256");
|
|
554
|
+
} catch (java.security.NoSuchAlgorithmException e) {
|
|
555
|
+
System.err.println(TAG + " SHA-256 algorithm not available");
|
|
556
|
+
return "";
|
|
555
557
|
}
|
|
556
558
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
559
|
+
try (FileInputStream fis = new FileInputStream(file)) {
|
|
560
|
+
byte[] buffer = new byte[BUFFER_SIZE];
|
|
561
|
+
int length;
|
|
562
|
+
while ((length = fis.read(buffer)) != -1) {
|
|
563
|
+
digest.update(buffer, 0, length);
|
|
564
|
+
}
|
|
565
|
+
byte[] hash = digest.digest();
|
|
566
|
+
StringBuilder hexString = new StringBuilder();
|
|
567
|
+
for (byte b : hash) {
|
|
568
|
+
String hex = Integer.toHexString(0xff & b);
|
|
569
|
+
if (hex.length() == 1) hexString.append('0');
|
|
570
|
+
hexString.append(hex);
|
|
571
|
+
}
|
|
572
|
+
return hexString.toString();
|
|
573
|
+
} catch (IOException e) {
|
|
574
|
+
System.err.println(
|
|
575
|
+
TAG +
|
|
576
|
+
" Cannot calc checksum v2: " +
|
|
577
|
+
file.getPath() +
|
|
578
|
+
" " +
|
|
579
|
+
e.getMessage()
|
|
562
580
|
);
|
|
581
|
+
return "";
|
|
563
582
|
}
|
|
583
|
+
}
|
|
564
584
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
final FileInputStream fis = new FileInputStream(file);
|
|
577
|
-
final BufferedInputStream bis = new BufferedInputStream(fis);
|
|
578
|
-
final DataInputStream dis = new DataInputStream(bis)
|
|
585
|
+
private void decryptFileV2(
|
|
586
|
+
final File file,
|
|
587
|
+
final String ivSessionKey,
|
|
588
|
+
final String version
|
|
589
|
+
) throws IOException {
|
|
590
|
+
// (str != null && !str.isEmpty())
|
|
591
|
+
if (
|
|
592
|
+
this.publicKey.isEmpty() ||
|
|
593
|
+
ivSessionKey == null ||
|
|
594
|
+
ivSessionKey.isEmpty() ||
|
|
595
|
+
ivSessionKey.split(":").length != 2
|
|
579
596
|
) {
|
|
580
|
-
|
|
581
|
-
|
|
597
|
+
Log.i(TAG, "Cannot found public key or sessionKey");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (!this.publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
601
|
+
Log.e(
|
|
602
|
+
CapacitorUpdater.TAG,
|
|
603
|
+
"The public key is not a valid RSA Public key"
|
|
604
|
+
);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
String ivB64 = ivSessionKey.split(":")[0];
|
|
609
|
+
String sessionKeyB64 = ivSessionKey.split(":")[1];
|
|
610
|
+
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
|
|
611
|
+
byte[] sessionKey = Base64.decode(
|
|
612
|
+
sessionKeyB64.getBytes(),
|
|
613
|
+
Base64.DEFAULT
|
|
614
|
+
);
|
|
615
|
+
PublicKey pKey = CryptoCipherV2.stringToPublicKey(this.publicKey);
|
|
616
|
+
byte[] decryptedSessionKey = CryptoCipherV2.decryptRSA(sessionKey, pKey);
|
|
582
617
|
|
|
583
|
-
|
|
584
|
-
|
|
618
|
+
SecretKey sKey = CryptoCipherV2.byteToSessionKey(decryptedSessionKey);
|
|
619
|
+
byte[] content = new byte[(int) file.length()];
|
|
620
|
+
|
|
621
|
+
try (
|
|
622
|
+
final FileInputStream fis = new FileInputStream(file);
|
|
623
|
+
final BufferedInputStream bis = new BufferedInputStream(fis);
|
|
624
|
+
final DataInputStream dis = new DataInputStream(bis)
|
|
625
|
+
) {
|
|
626
|
+
dis.readFully(content);
|
|
627
|
+
dis.close();
|
|
628
|
+
byte[] decrypted = CryptoCipherV2.decryptAES(content, sKey, iv);
|
|
629
|
+
// write the decrypted string to the file
|
|
630
|
+
try (
|
|
631
|
+
final FileOutputStream fos = new FileOutputStream(
|
|
632
|
+
file.getAbsolutePath()
|
|
633
|
+
)
|
|
634
|
+
) {
|
|
635
|
+
fos.write(decrypted);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} catch (GeneralSecurityException e) {
|
|
639
|
+
Log.i(TAG, "decryptFile fail");
|
|
640
|
+
this.sendStats("decrypt_fail", version);
|
|
641
|
+
e.printStackTrace();
|
|
642
|
+
throw new IOException("GeneralSecurityException");
|
|
585
643
|
}
|
|
586
644
|
}
|
|
587
645
|
|
|
@@ -591,14 +649,15 @@ public class CapacitorUpdater {
|
|
|
591
649
|
final String version
|
|
592
650
|
) throws IOException {
|
|
593
651
|
// (str != null && !str.isEmpty())
|
|
594
|
-
if (
|
|
595
|
-
|
|
596
|
-
|
|
652
|
+
if (this.privateKey == null || this.privateKey.isEmpty()) {
|
|
653
|
+
Log.i(TAG, "Cannot found privateKey");
|
|
654
|
+
return;
|
|
655
|
+
} else if (
|
|
597
656
|
ivSessionKey == null ||
|
|
598
657
|
ivSessionKey.isEmpty() ||
|
|
599
658
|
ivSessionKey.split(":").length != 2
|
|
600
659
|
) {
|
|
601
|
-
Log.i(TAG, "Cannot found
|
|
660
|
+
Log.i(TAG, "Cannot found sessionKey");
|
|
602
661
|
return;
|
|
603
662
|
}
|
|
604
663
|
try {
|
|
@@ -643,8 +702,7 @@ public class CapacitorUpdater {
|
|
|
643
702
|
final String url,
|
|
644
703
|
final String version,
|
|
645
704
|
final String sessionKey,
|
|
646
|
-
final String checksum
|
|
647
|
-
final String signature
|
|
705
|
+
final String checksum
|
|
648
706
|
) {
|
|
649
707
|
final String id = this.randomString();
|
|
650
708
|
this.saveBundleInfo(
|
|
@@ -665,8 +723,7 @@ public class CapacitorUpdater {
|
|
|
665
723
|
version,
|
|
666
724
|
sessionKey,
|
|
667
725
|
checksum,
|
|
668
|
-
this.randomString()
|
|
669
|
-
signature
|
|
726
|
+
this.randomString()
|
|
670
727
|
);
|
|
671
728
|
}
|
|
672
729
|
|
|
@@ -674,8 +731,7 @@ public class CapacitorUpdater {
|
|
|
674
731
|
final String url,
|
|
675
732
|
final String version,
|
|
676
733
|
final String sessionKey,
|
|
677
|
-
final String checksum
|
|
678
|
-
final String signature
|
|
734
|
+
final String checksum
|
|
679
735
|
) throws IOException {
|
|
680
736
|
final String id = this.randomString();
|
|
681
737
|
this.saveBundleInfo(
|
|
@@ -693,15 +749,7 @@ public class CapacitorUpdater {
|
|
|
693
749
|
final String dest = this.randomString();
|
|
694
750
|
this.downloadFile(id, url, dest);
|
|
695
751
|
final Boolean finished =
|
|
696
|
-
this.finishDownload(
|
|
697
|
-
id,
|
|
698
|
-
dest,
|
|
699
|
-
version,
|
|
700
|
-
sessionKey,
|
|
701
|
-
checksum,
|
|
702
|
-
signature,
|
|
703
|
-
false
|
|
704
|
-
);
|
|
752
|
+
this.finishDownload(id, dest, version, sessionKey, checksum, false);
|
|
705
753
|
final BundleStatus status = finished
|
|
706
754
|
? BundleStatus.PENDING
|
|
707
755
|
: BundleStatus.ERROR;
|
|
@@ -30,7 +30,6 @@ import java.io.IOException;
|
|
|
30
30
|
import java.lang.reflect.Type;
|
|
31
31
|
import java.net.MalformedURLException;
|
|
32
32
|
import java.net.URL;
|
|
33
|
-
import java.security.PublicKey;
|
|
34
33
|
import java.text.SimpleDateFormat;
|
|
35
34
|
import java.util.ArrayList;
|
|
36
35
|
import java.util.Date;
|
|
@@ -51,12 +50,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
51
50
|
private static final String updateUrlDefault =
|
|
52
51
|
"https://api.capgo.app/updates";
|
|
53
52
|
private static final String statsUrlDefault = "https://api.capgo.app/stats";
|
|
54
|
-
private static final String defaultPrivateKey =
|
|
55
|
-
"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4pW9olT0FBXXivRCzd3xcImlWZrqkwcF2xTkX/FwXmj9eh9H\nkBLrsQmfsC+PJisRXIOGq6a0z3bsGq6jBpp3/Jr9jiaW5VuPGaKeMaZZBRvi/N5f\nIMG3hZXSOcy0IYg+E1Q7RkYO1xq5GLHseqG+PXvJsNe4R8R/Bmd/ngq0xh/cvcrH\nHpXwO0Aj9tfprlb+rHaVV79EkVRWYPidOLnK1n0EFHFJ1d/MyDIp10TEGm2xHpf/\nBrlb1an8wXEuzoC0DgYaczgTjovwR+ewSGhSHJliQdM0Qa3o1iN87DldWtydImMs\nPjJ3DUwpsjAMRe5X8Et4+udFW2ciYnQo9H0CkwIDAQABAoIBAQCtjlMV/4qBxAU4\nu0ZcWA9yywwraX0aJ3v1xrfzQYV322Wk4Ea5dbSxA5UcqCE29DA1M824t1Wxv/6z\npWbcTP9xLuresnJMtmgTE7umfiubvTONy2sENT20hgDkIwcq1CfwOEm61zjQzPhQ\nkSB5AmEsyR/BZEsUNc+ygR6AWOUFB7tj4yMc32LOTWSbE/znnF2BkmlmnQykomG1\n2oVqM3lUFP7+m8ux1O7scO6IMts+Z/eFXjWfxpbebUSvSIR83GXPQZ34S/c0ehOg\nyHdmCSOel1r3VvInMe+30j54Jr+Ml/7Ee6axiwyE2e/bd85MsK9sVdp0OtelXaqA\nOZZqWvN5AoGBAP2Hn3lSq+a8GsDH726mHJw60xM0LPbVJTYbXsmQkg1tl3NKJTMM\nQqz41+5uys+phEgLHI9gVJ0r+HaGHXnJ4zewlFjsudstb/0nfctUvTqnhEhfNo9I\ny4kufVKPRF3sMEeo7CDVJs4GNBLycEyIBy6Mbv0VcO7VaZqggRwu4no9AoGBAOTK\n6NWYs1BWlkua2wmxexGOzehNGedInp0wGr2l4FDayWjkZLqvB+nNXUQ63NdHlSs4\nWB2Z1kQXZxVaI2tPYexGUKXEo2uFob63uflbuE029ovDXIIPFTPtGNdNXwhHT5a+\nPhmy3sMc+s2BSNM5qaNmfxQxhdd6gRU6oikE+c0PAoGAMn3cKNFqIt27hkFLUgIL\nGKIuf1iYy9/PNWNmEUaVj88PpopRtkTu0nwMpROzmH/uNFriKTvKHjMvnItBO4wV\nkHW+VadvrFL0Rrqituf9d7z8/1zXBNo+juePVe3qc7oiM2NVA4Tv4YAixtM5wkQl\nCgQ15nlqsGYYTg9BJ1e/CxECgYEAjEYPzO2reuUrjr0p8F59ev1YJ0YmTJRMk0ks\nC/yIdGo/tGzbiU3JB0LfHPcN8Xu07GPGOpfYM7U5gXDbaG6qNgfCaHAQVdr/mQPi\nJQ1kCQtay8QCkscWk9iZM1//lP7LwDtxraXqSCwbZSYP9VlUNZeg8EuQqNU2EUL6\nqzWexmcCgYEA0prUGNBacraTYEknB1CsbP36UPWsqFWOvevlz+uEC5JPxPuW5ZHh\nSQN7xl6+PHyjPBM7ttwPKyhgLOVTb3K7ex/PXnudojMUK5fh7vYfChVTSlx2p6r0\nDi58PdD+node08cJH+ie0Yphp7m+D4+R9XD0v0nEvnu4BtAW6DrJasw=\n-----END RSA PRIVATE KEY-----\n";
|
|
56
53
|
private static final String channelUrlDefault =
|
|
57
54
|
"https://api.capgo.app/channel_self";
|
|
58
55
|
|
|
59
|
-
private final String PLUGIN_VERSION = "6.1.
|
|
56
|
+
private final String PLUGIN_VERSION = "6.1.19";
|
|
60
57
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
61
58
|
|
|
62
59
|
private SharedPreferences.Editor editor;
|
|
@@ -138,12 +135,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
138
135
|
this.implementation.requestQueue = Volley.newRequestQueue(
|
|
139
136
|
this.getContext()
|
|
140
137
|
);
|
|
141
|
-
final String signKeyStr = this.getConfig().getString("signKey", "");
|
|
142
|
-
if (signKeyStr.length() > 0) {
|
|
143
|
-
this.implementation.signKey = CryptoCipher.stringToPublicKey(
|
|
144
|
-
signKeyStr
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
138
|
|
|
148
139
|
this.implementation.directUpdate = this.getConfig()
|
|
149
140
|
.getBoolean("directUpdate", false);
|
|
@@ -181,8 +172,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
181
172
|
);
|
|
182
173
|
}
|
|
183
174
|
Log.i(CapacitorUpdater.TAG, "appId: " + implementation.appId);
|
|
175
|
+
this.implementation.publicKey = this.getConfig().getString("publicKey", "");
|
|
184
176
|
this.implementation.privateKey = this.getConfig()
|
|
185
|
-
.getString("privateKey",
|
|
177
|
+
.getString("privateKey", "");
|
|
178
|
+
if (
|
|
179
|
+
this.implementation.privateKey != null &&
|
|
180
|
+
!this.implementation.privateKey.isEmpty()
|
|
181
|
+
) {
|
|
182
|
+
this.implementation.hasOldPrivateKeyPropertyInConfig = true;
|
|
183
|
+
}
|
|
186
184
|
this.implementation.statsUrl = this.getConfig()
|
|
187
185
|
.getString("statsUrl", statsUrlDefault);
|
|
188
186
|
this.implementation.channelUrl = this.getConfig()
|
|
@@ -583,7 +581,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
583
581
|
final String version = call.getString("version");
|
|
584
582
|
final String sessionKey = call.getString("sessionKey", "");
|
|
585
583
|
final String checksum = call.getString("checksum", "");
|
|
586
|
-
final String signature = call.getString("signature", "");
|
|
587
584
|
if (url == null) {
|
|
588
585
|
Log.e(CapacitorUpdater.TAG, "Download called without url");
|
|
589
586
|
call.reject("Download called without url");
|
|
@@ -594,17 +591,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
594
591
|
call.reject("Download called without version");
|
|
595
592
|
return;
|
|
596
593
|
}
|
|
597
|
-
if (
|
|
598
|
-
this.implementation.signKey != null &&
|
|
599
|
-
(signature == null || signature.isEmpty())
|
|
600
|
-
) {
|
|
601
|
-
Log.e(
|
|
602
|
-
CapacitorUpdater.TAG,
|
|
603
|
-
"Signature required but none provided for download call"
|
|
604
|
-
);
|
|
605
|
-
call.reject("Signature required but none provided");
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
594
|
try {
|
|
609
595
|
Log.i(CapacitorUpdater.TAG, "Downloading " + url);
|
|
610
596
|
startNewThread(() -> {
|
|
@@ -614,8 +600,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
614
600
|
url,
|
|
615
601
|
version,
|
|
616
602
|
sessionKey,
|
|
617
|
-
checksum
|
|
618
|
-
signature
|
|
603
|
+
checksum
|
|
619
604
|
);
|
|
620
605
|
if (downloaded.isErrorStatus()) {
|
|
621
606
|
throw new RuntimeException(
|
|
@@ -1283,15 +1268,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1283
1268
|
final String checksum = res.has("checksum")
|
|
1284
1269
|
? res.getString("checksum")
|
|
1285
1270
|
: "";
|
|
1286
|
-
final String signature = res.has("signature")
|
|
1287
|
-
? res.getString("signature")
|
|
1288
|
-
: "";
|
|
1289
1271
|
CapacitorUpdaterPlugin.this.implementation.downloadBackground(
|
|
1290
1272
|
url,
|
|
1291
1273
|
latestVersionName,
|
|
1292
1274
|
sessionKey,
|
|
1293
|
-
checksum
|
|
1294
|
-
signature
|
|
1275
|
+
checksum
|
|
1295
1276
|
);
|
|
1296
1277
|
} catch (final Exception e) {
|
|
1297
1278
|
Log.e(CapacitorUpdater.TAG, "error downloading file", e);
|
|
@@ -70,17 +70,12 @@ public class CryptoCipherV2 {
|
|
|
70
70
|
|
|
71
71
|
public static PublicKey stringToPublicKey(String public_key)
|
|
72
72
|
throws GeneralSecurityException {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
pkcs1Pem = pkcs1Pem.replace("-----END RSA PUBLIC KEY-----", "");
|
|
78
|
-
pkcs1Pem = pkcs1Pem.replace("\\n", "");
|
|
79
|
-
pkcs1Pem = pkcs1Pem.replace(" ", "");
|
|
73
|
+
String pkcs1Pem = public_key
|
|
74
|
+
.replaceAll("\\s+", "")
|
|
75
|
+
.replace("-----BEGINRSAPUBLICKEY-----", "")
|
|
76
|
+
.replace("-----ENDRSAPUBLICKEY-----", "");
|
|
80
77
|
|
|
81
78
|
byte[] pkcs1EncodedBytes = Base64.decode(pkcs1Pem, Base64.DEFAULT);
|
|
82
|
-
|
|
83
|
-
// extract the public key
|
|
84
79
|
return readPkcs1PublicKey(pkcs1EncodedBytes);
|
|
85
80
|
}
|
|
86
81
|
|
|
@@ -25,7 +25,6 @@ public class DownloadService extends IntentService {
|
|
|
25
25
|
public static final String VERSION = "version";
|
|
26
26
|
public static final String SESSIONKEY = "sessionkey";
|
|
27
27
|
public static final String CHECKSUM = "checksum";
|
|
28
|
-
public static final String SIGNATURE = "signature";
|
|
29
28
|
public static final String NOTIFICATION = "service receiver";
|
|
30
29
|
public static final String PERCENTDOWNLOAD = "percent receiver";
|
|
31
30
|
private static final String UPDATE_FILE = "update.dat";
|
|
@@ -54,7 +53,6 @@ public class DownloadService extends IntentService {
|
|
|
54
53
|
String version = intent.getStringExtra(VERSION);
|
|
55
54
|
String sessionKey = intent.getStringExtra(SESSIONKEY);
|
|
56
55
|
String checksum = intent.getStringExtra(CHECKSUM);
|
|
57
|
-
String signature = intent.getStringExtra(SIGNATURE);
|
|
58
56
|
|
|
59
57
|
File target = new File(documentsDir, dest);
|
|
60
58
|
File infoFile = new File(documentsDir, UPDATE_FILE); // The file where the download progress (how much byte
|
|
@@ -140,22 +138,14 @@ public class DownloadService extends IntentService {
|
|
|
140
138
|
// Rename the temp file with the final name (dest)
|
|
141
139
|
tempFile.renameTo(new File(documentsDir, dest));
|
|
142
140
|
infoFile.delete();
|
|
143
|
-
publishResults(dest, id, version, checksum, sessionKey,
|
|
141
|
+
publishResults(dest, id, version, checksum, sessionKey, "");
|
|
144
142
|
} else {
|
|
145
143
|
infoFile.delete();
|
|
146
144
|
}
|
|
147
145
|
httpConn.disconnect();
|
|
148
146
|
} catch (OutOfMemoryError e) {
|
|
149
147
|
e.printStackTrace();
|
|
150
|
-
publishResults(
|
|
151
|
-
"",
|
|
152
|
-
id,
|
|
153
|
-
version,
|
|
154
|
-
checksum,
|
|
155
|
-
sessionKey,
|
|
156
|
-
signature,
|
|
157
|
-
"low_mem_fail"
|
|
158
|
-
);
|
|
148
|
+
publishResults("", id, version, checksum, sessionKey, "low_mem_fail");
|
|
159
149
|
} catch (Exception e) {
|
|
160
150
|
e.printStackTrace();
|
|
161
151
|
publishResults(
|
|
@@ -164,7 +154,6 @@ public class DownloadService extends IntentService {
|
|
|
164
154
|
version,
|
|
165
155
|
checksum,
|
|
166
156
|
sessionKey,
|
|
167
|
-
signature,
|
|
168
157
|
e.getLocalizedMessage()
|
|
169
158
|
);
|
|
170
159
|
}
|
|
@@ -197,7 +186,6 @@ public class DownloadService extends IntentService {
|
|
|
197
186
|
String version,
|
|
198
187
|
String checksum,
|
|
199
188
|
String sessionKey,
|
|
200
|
-
String signature,
|
|
201
189
|
String error
|
|
202
190
|
) {
|
|
203
191
|
Intent intent = new Intent(NOTIFICATION);
|
|
@@ -210,7 +198,6 @@ public class DownloadService extends IntentService {
|
|
|
210
198
|
intent.putExtra(VERSION, version);
|
|
211
199
|
intent.putExtra(SESSIONKEY, sessionKey);
|
|
212
200
|
intent.putExtra(CHECKSUM, checksum);
|
|
213
|
-
intent.putExtra(SIGNATURE, signature);
|
|
214
201
|
sendBroadcast(intent);
|
|
215
202
|
}
|
|
216
203
|
}
|
package/dist/docs.json
CHANGED
|
@@ -1091,22 +1091,6 @@
|
|
|
1091
1091
|
"docs": "The checksum for the update",
|
|
1092
1092
|
"complexTypes": [],
|
|
1093
1093
|
"type": "string | undefined"
|
|
1094
|
-
},
|
|
1095
|
-
{
|
|
1096
|
-
"name": "signature",
|
|
1097
|
-
"tags": [
|
|
1098
|
-
{
|
|
1099
|
-
"text": "6.1.0",
|
|
1100
|
-
"name": "since"
|
|
1101
|
-
},
|
|
1102
|
-
{
|
|
1103
|
-
"text": "undefined",
|
|
1104
|
-
"name": "default"
|
|
1105
|
-
}
|
|
1106
|
-
],
|
|
1107
|
-
"docs": "The signature of the update. Can be generated using capgo CLI",
|
|
1108
|
-
"complexTypes": [],
|
|
1109
|
-
"type": "string | undefined"
|
|
1110
1094
|
}
|
|
1111
1095
|
]
|
|
1112
1096
|
},
|
|
@@ -2063,22 +2047,6 @@
|
|
|
2063
2047
|
"docs": "Set the default channel for the app in the config.",
|
|
2064
2048
|
"complexTypes": [],
|
|
2065
2049
|
"type": "string | undefined"
|
|
2066
|
-
},
|
|
2067
|
-
{
|
|
2068
|
-
"name": "signKey",
|
|
2069
|
-
"tags": [
|
|
2070
|
-
{
|
|
2071
|
-
"text": "undefined",
|
|
2072
|
-
"name": "default"
|
|
2073
|
-
},
|
|
2074
|
-
{
|
|
2075
|
-
"text": "6.1.0",
|
|
2076
|
-
"name": "since"
|
|
2077
|
-
}
|
|
2078
|
-
],
|
|
2079
|
-
"docs": "Public key used for bundle signing.",
|
|
2080
|
-
"complexTypes": [],
|
|
2081
|
-
"type": "string | undefined"
|
|
2082
2050
|
}
|
|
2083
2051
|
],
|
|
2084
2052
|
"docs": "CapacitorUpdater can be configured with these options:"
|
|
@@ -170,15 +170,6 @@ declare module "@capacitor/cli" {
|
|
|
170
170
|
* @since 5.5.0
|
|
171
171
|
*/
|
|
172
172
|
defaultChannel?: string;
|
|
173
|
-
/**
|
|
174
|
-
* Public key used for bundle signing.
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* @default undefined
|
|
179
|
-
* @since 6.1.0
|
|
180
|
-
*/
|
|
181
|
-
signKey?: string;
|
|
182
173
|
};
|
|
183
174
|
}
|
|
184
175
|
}
|
|
@@ -607,12 +598,6 @@ export interface DownloadOptions {
|
|
|
607
598
|
* @default undefined
|
|
608
599
|
*/
|
|
609
600
|
checksum?: string;
|
|
610
|
-
/**
|
|
611
|
-
* The signature of the update. Can be generated using capgo CLI
|
|
612
|
-
* @since 6.1.0
|
|
613
|
-
* @default undefined
|
|
614
|
-
*/
|
|
615
|
-
signature?: string;
|
|
616
601
|
}
|
|
617
602
|
export interface BundleId {
|
|
618
603
|
id: string;
|