@capgo/cordova-updater 8.1.4 → 8.2.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.
- package/dist/esm/definitions.d.ts +116 -86
- package/dist/esm/definitions.js +2 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/exec.js +11 -1
- package/dist/esm/exec.js.map +1 -1
- package/dist/esm/history.js +7 -7
- package/dist/esm/history.js.map +1 -1
- package/dist/plugin.cjs.js +13 -3
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +13 -3
- package/dist/plugin.js.map +1 -1
- package/package.json +5 -3
- package/plugin.xml +9 -9
- package/src/android/CordovaUpdaterPlugin.java +173 -5
- package/src/android/app/capgo/cordova/updater/CapgoUpdater.java +313 -55
- package/src/android/app/capgo/cordova/updater/CryptoCipher.java +51 -18
- package/src/android/app/capgo/cordova/updater/DelayUpdateUtils.java +0 -1
- package/src/android/app/capgo/cordova/updater/DownloadService.java +402 -89
- package/src/android/app/capgo/cordova/updater/Version.java +312 -0
- package/src/android/capgo-cordova-updater.gradle +3 -5
- package/src/android/test/app/capgo/cordova/updater/BackgroundDownloadSettlementTest.java +29 -0
- package/src/android/test/app/capgo/cordova/updater/CapacitorUpdaterUnitTest.java.skip +2 -12
- package/src/android/test/app/capgo/cordova/updater/DelayUpdateUtilsTest.java +0 -1
- package/src/android/test/app/capgo/cordova/updater/RsaContractTest.java +137 -0
- package/src/android/test/app/capgo/cordova/updater/SecurityHardeningTest.java +197 -0
- package/src/android/test/app/capgo/cordova/updater/SessionKeyRequiredTest.java +344 -0
- package/src/ios/CapgoSemanticVersion.swift +181 -0
- package/src/ios/CapgoUpdater.swift +427 -114
- package/src/ios/CordovaUpdaterPlugin.swift +171 -40
- package/src/ios/CryptoCipher.swift +41 -38
- package/src/ios/DelayUpdateUtils.swift +19 -45
- package/src/ios/Logger.swift +81 -5
- package/src/ios/RedirectPolicyDelegate.swift +53 -0
- package/src/ios/WebViewStatsReporter.swift +5 -0
- package/src/ios/ZipArchiveReader.swift +263 -0
- package/src/ios/ZipCentralDirectory.swift +283 -0
|
@@ -50,17 +50,15 @@ public class CryptoCipher {
|
|
|
50
50
|
return decryptedBytes;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
57
|
-
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
58
|
-
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
59
|
-
return cipher.doFinal(cipherText);
|
|
60
|
-
} catch (Exception e) {
|
|
61
|
-
e.printStackTrace();
|
|
53
|
+
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) throws GeneralSecurityException {
|
|
54
|
+
if (key == null || iv == null) {
|
|
55
|
+
throw new IllegalArgumentException("AES key and IV must not be null");
|
|
62
56
|
}
|
|
63
|
-
|
|
57
|
+
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
58
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
59
|
+
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
60
|
+
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
61
|
+
return cipher.doFinal(cipherText);
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
public static SecretKey byteToSessionKey(byte[] sessionKey) {
|
|
@@ -137,13 +135,25 @@ public class CryptoCipher {
|
|
|
137
135
|
return buf;
|
|
138
136
|
}
|
|
139
137
|
|
|
138
|
+
public static boolean isValidSessionKey(final String sessionKey) {
|
|
139
|
+
if (sessionKey == null || sessionKey.isEmpty()) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
String[] sessionKeyParts = sessionKey.split(":", -1);
|
|
143
|
+
return sessionKeyParts.length == 2 && !sessionKeyParts[0].isEmpty() && !sessionKeyParts[1].isEmpty();
|
|
144
|
+
}
|
|
145
|
+
|
|
140
146
|
public static void decryptFile(final File file, final String publicKey, final String ivSessionKey) throws IOException {
|
|
141
|
-
if (publicKey.isEmpty() ||
|
|
142
|
-
logger
|
|
147
|
+
if (publicKey.isEmpty() || !isValidSessionKey(ivSessionKey)) {
|
|
148
|
+
if (logger != null) {
|
|
149
|
+
logger.info("Encryption not set, no public key or session, ignored");
|
|
150
|
+
}
|
|
143
151
|
return;
|
|
144
152
|
}
|
|
145
153
|
if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
146
|
-
logger
|
|
154
|
+
if (logger != null) {
|
|
155
|
+
logger.error("The public key is not a valid RSA Public key");
|
|
156
|
+
}
|
|
147
157
|
return;
|
|
148
158
|
}
|
|
149
159
|
|
|
@@ -152,29 +162,50 @@ public class CryptoCipher {
|
|
|
152
162
|
String sessionKeyB64 = ivSessionKey.split(":")[1];
|
|
153
163
|
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
|
|
154
164
|
byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
|
|
165
|
+
if (iv.length != 16) {
|
|
166
|
+
throw new IOException("AES file decryption failed: IV must be 16 bytes");
|
|
167
|
+
}
|
|
155
168
|
PublicKey pKey = CryptoCipher.stringToPublicKey(publicKey);
|
|
156
169
|
byte[] decryptedSessionKey = CryptoCipher.decryptRSA(sessionKey, pKey);
|
|
170
|
+
if (decryptedSessionKey == null || decryptedSessionKey.length != 16) {
|
|
171
|
+
throw new IOException("AES file decryption failed: decrypted session key must be 16 bytes");
|
|
172
|
+
}
|
|
157
173
|
|
|
158
174
|
SecretKey sKey = CryptoCipher.byteToSessionKey(decryptedSessionKey);
|
|
159
175
|
decryptAesFile(file, sKey, iv);
|
|
160
176
|
} catch (GeneralSecurityException e) {
|
|
161
|
-
logger
|
|
162
|
-
|
|
177
|
+
if (logger != null) {
|
|
178
|
+
logger.info("decryptFile fail");
|
|
179
|
+
}
|
|
180
|
+
throw new IOException("AES file decryption failed: " + e.getMessage(), e);
|
|
163
181
|
}
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
static void decryptAesFile(File file, SecretKey key, byte[] iv) throws IOException
|
|
184
|
+
static void decryptAesFile(File file, SecretKey key, byte[] iv) throws IOException {
|
|
185
|
+
if (key == null) {
|
|
186
|
+
throw new IOException("AES file decryption failed: missing session key");
|
|
187
|
+
}
|
|
188
|
+
if (iv == null) {
|
|
189
|
+
throw new IOException("AES file decryption failed: missing IV");
|
|
190
|
+
}
|
|
191
|
+
if (iv.length != 16) {
|
|
192
|
+
throw new IOException("AES file decryption failed: IV must be 16 bytes");
|
|
193
|
+
}
|
|
194
|
+
byte[] keyBytes = key.getEncoded();
|
|
195
|
+
if (keyBytes == null || keyBytes.length != 16) {
|
|
196
|
+
throw new IOException("AES file decryption failed: session key must be 16 bytes");
|
|
197
|
+
}
|
|
167
198
|
if (file.length() == 0) {
|
|
168
199
|
throw new IOException("Empty encrypted data");
|
|
169
200
|
}
|
|
170
|
-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
171
|
-
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getEncoded(), "AES"), new IvParameterSpec(iv));
|
|
172
201
|
File parent = file.getAbsoluteFile().getParentFile();
|
|
173
202
|
if (parent == null) {
|
|
174
203
|
throw new IOException("Cannot create temp file for " + file.getAbsolutePath());
|
|
175
204
|
}
|
|
176
205
|
File tempFile = File.createTempFile("capgo-aes-", ".tmp", parent);
|
|
177
206
|
try {
|
|
207
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
208
|
+
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(iv));
|
|
178
209
|
byte[] inBuf = new byte[ioBufferBytes()];
|
|
179
210
|
// Reuse one output buffer. cipher.update(in) allocates a new byte[] per chunk.
|
|
180
211
|
byte[] outBuf = new byte[inBuf.length + 16];
|
|
@@ -196,6 +227,8 @@ public class CryptoCipher {
|
|
|
196
227
|
}
|
|
197
228
|
replaceFile(tempFile, file);
|
|
198
229
|
tempFile = null;
|
|
230
|
+
} catch (GeneralSecurityException | IllegalArgumentException e) {
|
|
231
|
+
throw new IOException("AES file decryption failed: " + e.getMessage(), e);
|
|
199
232
|
} finally {
|
|
200
233
|
if (tempFile != null && tempFile.exists()) {
|
|
201
234
|
tempFile.delete();
|