@capgo/capacitor-updater 6.14.0 → 6.14.2
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 +11 -11
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +134 -194
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +23 -23
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +960 -1165
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1259 -1629
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +161 -197
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +202 -256
- package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +16 -16
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +44 -48
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +4 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +378 -467
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +71 -83
- package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +19 -28
- package/dist/docs.json +17 -17
- package/dist/esm/definitions.d.ts +13 -13
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +2 -2
- package/dist/esm/web.js +43 -43
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +43 -43
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +43 -43
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/CapacitorUpdater.swift +8 -8
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
- package/package.json +5 -7
|
@@ -38,293 +38,239 @@ import javax.crypto.spec.SecretKeySpec;
|
|
|
38
38
|
|
|
39
39
|
public class CryptoCipherV2 {
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
|
|
50
|
-
try {
|
|
51
|
-
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
52
|
-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
53
|
-
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
54
|
-
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
55
|
-
return cipher.doFinal(cipherText);
|
|
56
|
-
} catch (Exception e) {
|
|
57
|
-
e.printStackTrace();
|
|
41
|
+
public static byte[] decryptRSA(byte[] source, PublicKey publicKey)
|
|
42
|
+
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
43
|
+
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
|
44
|
+
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
|
45
|
+
byte[] decryptedBytes = cipher.doFinal(source);
|
|
46
|
+
return decryptedBytes;
|
|
58
47
|
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
|
|
50
|
+
try {
|
|
51
|
+
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
52
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
53
|
+
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
54
|
+
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
55
|
+
return cipher.doFinal(cipherText);
|
|
56
|
+
} catch (Exception e) {
|
|
57
|
+
e.printStackTrace();
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509Bytes);
|
|
71
|
-
try {
|
|
72
|
-
return keyFactory.generatePublic(keySpec);
|
|
73
|
-
} catch (InvalidKeySpecException e) {
|
|
74
|
-
throw new IllegalArgumentException("Unexpected key format!", e);
|
|
62
|
+
public static SecretKey byteToSessionKey(byte[] sessionKey) {
|
|
63
|
+
// rebuild key using SecretKeySpec
|
|
64
|
+
return new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
|
|
75
65
|
}
|
|
76
|
-
}
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
67
|
+
private static PublicKey readX509PublicKey(byte[] x509Bytes) throws GeneralSecurityException {
|
|
68
|
+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
69
|
+
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509Bytes);
|
|
70
|
+
try {
|
|
71
|
+
return keyFactory.generatePublic(keySpec);
|
|
72
|
+
} catch (InvalidKeySpecException e) {
|
|
73
|
+
throw new IllegalArgumentException("Unexpected key format!", e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
84
76
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
public static PublicKey stringToPublicKey(String public_key) throws GeneralSecurityException {
|
|
78
|
+
String pkcs1Pem = public_key
|
|
79
|
+
.replaceAll("\\s+", "")
|
|
80
|
+
.replace("-----BEGINRSAPUBLICKEY-----", "")
|
|
81
|
+
.replace("-----ENDRSAPUBLICKEY-----", "");
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
private static final int SEQUENCE_TAG = 0x30;
|
|
93
|
-
private static final int BIT_STRING_TAG = 0x03;
|
|
94
|
-
private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
|
|
95
|
-
private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE = {
|
|
96
|
-
(byte) 0x30,
|
|
97
|
-
(byte) 0x0d,
|
|
98
|
-
(byte) 0x06,
|
|
99
|
-
(byte) 0x09,
|
|
100
|
-
(byte) 0x2a,
|
|
101
|
-
(byte) 0x86,
|
|
102
|
-
(byte) 0x48,
|
|
103
|
-
(byte) 0x86,
|
|
104
|
-
(byte) 0xf7,
|
|
105
|
-
(byte) 0x0d,
|
|
106
|
-
(byte) 0x01,
|
|
107
|
-
(byte) 0x01,
|
|
108
|
-
(byte) 0x01,
|
|
109
|
-
(byte) 0x05,
|
|
110
|
-
(byte) 0x00,
|
|
111
|
-
};
|
|
83
|
+
byte[] pkcs1EncodedBytes = Base64.decode(pkcs1Pem, Base64.DEFAULT);
|
|
84
|
+
return readPkcs1PublicKey(pkcs1EncodedBytes);
|
|
85
|
+
}
|
|
112
86
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
87
|
+
// since the public key is in pkcs1 format, we have to convert it to x509 format similar
|
|
88
|
+
// to what needs done with the private key converting to pkcs8 format
|
|
89
|
+
// so, the rest of the code below here is adapted from here https://stackoverflow.com/a/54246646
|
|
90
|
+
private static final int SEQUENCE_TAG = 0x30;
|
|
91
|
+
private static final int BIT_STRING_TAG = 0x03;
|
|
92
|
+
private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
|
|
93
|
+
private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE = {
|
|
94
|
+
(byte) 0x30,
|
|
95
|
+
(byte) 0x0d,
|
|
96
|
+
(byte) 0x06,
|
|
97
|
+
(byte) 0x09,
|
|
98
|
+
(byte) 0x2a,
|
|
99
|
+
(byte) 0x86,
|
|
100
|
+
(byte) 0x48,
|
|
101
|
+
(byte) 0x86,
|
|
102
|
+
(byte) 0xf7,
|
|
103
|
+
(byte) 0x0d,
|
|
104
|
+
(byte) 0x01,
|
|
105
|
+
(byte) 0x01,
|
|
106
|
+
(byte) 0x01,
|
|
107
|
+
(byte) 0x05,
|
|
108
|
+
(byte) 0x00
|
|
109
|
+
};
|
|
127
110
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
private static PublicKey readPkcs1PublicKey(byte[] pkcs1Bytes)
|
|
112
|
+
throws NoSuchAlgorithmException, InvalidKeySpecException, GeneralSecurityException {
|
|
113
|
+
// convert the pkcs1 public key to an x509 favorable format
|
|
114
|
+
byte[] keyBitString = createDEREncoding(BIT_STRING_TAG, joinPublic(NO_UNUSED_BITS, pkcs1Bytes));
|
|
115
|
+
byte[] keyInfoValue = joinPublic(RSA_ALGORITHM_IDENTIFIER_SEQUENCE, keyBitString);
|
|
116
|
+
byte[] keyInfoSequence = createDEREncoding(SEQUENCE_TAG, keyInfoValue);
|
|
117
|
+
return readX509PublicKey(keyInfoSequence);
|
|
132
118
|
}
|
|
133
119
|
|
|
134
|
-
byte[]
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
120
|
+
private static byte[] joinPublic(byte[]... bas) {
|
|
121
|
+
int len = 0;
|
|
122
|
+
for (int i = 0; i < bas.length; i++) {
|
|
123
|
+
len += bas[i].length;
|
|
124
|
+
}
|
|
140
125
|
|
|
141
|
-
|
|
142
|
-
|
|
126
|
+
byte[] buf = new byte[len];
|
|
127
|
+
int off = 0;
|
|
128
|
+
for (int i = 0; i < bas.length; i++) {
|
|
129
|
+
System.arraycopy(bas[i], 0, buf, off, bas[i].length);
|
|
130
|
+
off += bas[i].length;
|
|
131
|
+
}
|
|
143
132
|
|
|
144
|
-
|
|
145
|
-
final File file,
|
|
146
|
-
final String publicKey,
|
|
147
|
-
final String ivSessionKey
|
|
148
|
-
) throws IOException {
|
|
149
|
-
if (
|
|
150
|
-
publicKey.isEmpty() ||
|
|
151
|
-
ivSessionKey == null ||
|
|
152
|
-
ivSessionKey.isEmpty() ||
|
|
153
|
-
ivSessionKey.split(":").length != 2
|
|
154
|
-
) {
|
|
155
|
-
Log.i(CapacitorUpdater.TAG, "Cannot found public key or sessionKey");
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
159
|
-
Log.e(
|
|
160
|
-
CapacitorUpdater.TAG,
|
|
161
|
-
"The public key is not a valid RSA Public key"
|
|
162
|
-
);
|
|
163
|
-
return;
|
|
133
|
+
return buf;
|
|
164
134
|
}
|
|
165
135
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
byte[] decryptedSessionKey = CryptoCipherV2.decryptRSA(sessionKey, pKey);
|
|
136
|
+
public static void decryptFile(final File file, final String publicKey, final String ivSessionKey) throws IOException {
|
|
137
|
+
if (publicKey.isEmpty() || ivSessionKey == null || ivSessionKey.isEmpty() || ivSessionKey.split(":").length != 2) {
|
|
138
|
+
Log.i(CapacitorUpdater.TAG, "Cannot found public key or sessionKey");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
142
|
+
Log.e(CapacitorUpdater.TAG, "The public key is not a valid RSA Public key");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
176
145
|
|
|
177
|
-
|
|
178
|
-
|
|
146
|
+
try {
|
|
147
|
+
String ivB64 = ivSessionKey.split(":")[0];
|
|
148
|
+
String sessionKeyB64 = ivSessionKey.split(":")[1];
|
|
149
|
+
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
|
|
150
|
+
byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
|
|
151
|
+
PublicKey pKey = CryptoCipherV2.stringToPublicKey(publicKey);
|
|
152
|
+
byte[] decryptedSessionKey = CryptoCipherV2.decryptRSA(sessionKey, pKey);
|
|
179
153
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
final BufferedInputStream bis = new BufferedInputStream(fis);
|
|
183
|
-
final DataInputStream dis = new DataInputStream(bis)
|
|
184
|
-
) {
|
|
185
|
-
dis.readFully(content);
|
|
186
|
-
dis.close();
|
|
187
|
-
byte[] decrypted = CryptoCipherV2.decryptAES(content, sKey, iv);
|
|
188
|
-
// write the decrypted string to the file
|
|
189
|
-
try (
|
|
190
|
-
final FileOutputStream fos = new FileOutputStream(
|
|
191
|
-
file.getAbsolutePath()
|
|
192
|
-
)
|
|
193
|
-
) {
|
|
194
|
-
fos.write(decrypted);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
} catch (GeneralSecurityException e) {
|
|
198
|
-
Log.i(CapacitorUpdater.TAG, "decryptFile fail");
|
|
199
|
-
e.printStackTrace();
|
|
200
|
-
throw new IOException("GeneralSecurityException");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
154
|
+
SecretKey sKey = CryptoCipherV2.byteToSessionKey(decryptedSessionKey);
|
|
155
|
+
byte[] content = new byte[(int) file.length()];
|
|
203
156
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
157
|
+
try (
|
|
158
|
+
final FileInputStream fis = new FileInputStream(file);
|
|
159
|
+
final BufferedInputStream bis = new BufferedInputStream(fis);
|
|
160
|
+
final DataInputStream dis = new DataInputStream(bis)
|
|
161
|
+
) {
|
|
162
|
+
dis.readFully(content);
|
|
163
|
+
dis.close();
|
|
164
|
+
byte[] decrypted = CryptoCipherV2.decryptAES(content, sKey, iv);
|
|
165
|
+
// write the decrypted string to the file
|
|
166
|
+
try (final FileOutputStream fos = new FileOutputStream(file.getAbsolutePath())) {
|
|
167
|
+
fos.write(decrypted);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch (GeneralSecurityException e) {
|
|
171
|
+
Log.i(CapacitorUpdater.TAG, "decryptFile fail");
|
|
172
|
+
e.printStackTrace();
|
|
173
|
+
throw new IOException("GeneralSecurityException");
|
|
174
|
+
}
|
|
220
175
|
}
|
|
221
|
-
}
|
|
222
176
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
} catch (GeneralSecurityException e) {
|
|
240
|
-
Log.e(CapacitorUpdater.TAG, "decryptChecksum fail: " + e.getMessage());
|
|
241
|
-
throw new IOException("Decryption failed: " + e.getMessage());
|
|
177
|
+
public static String decryptChecksum(String checksum, String publicKey) throws IOException {
|
|
178
|
+
if (publicKey.isEmpty()) {
|
|
179
|
+
Log.e(CapacitorUpdater.TAG, "The public key is empty");
|
|
180
|
+
return checksum;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
byte[] checksumBytes = Base64.decode(checksum, Base64.DEFAULT);
|
|
184
|
+
PublicKey pKey = CryptoCipherV2.stringToPublicKey(publicKey);
|
|
185
|
+
byte[] decryptedChecksum = CryptoCipherV2.decryptRSA(checksumBytes, pKey);
|
|
186
|
+
// return Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
187
|
+
String result = Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
188
|
+
return result.replaceAll("\\s", ""); // Remove all whitespace, including newlines
|
|
189
|
+
} catch (GeneralSecurityException e) {
|
|
190
|
+
Log.e(CapacitorUpdater.TAG, "decryptChecksum fail: " + e.getMessage());
|
|
191
|
+
throw new IOException("Decryption failed: " + e.getMessage());
|
|
192
|
+
}
|
|
242
193
|
}
|
|
243
|
-
}
|
|
244
194
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
195
|
+
public static String decryptChecksum(String checksum, String publicKey, String version) throws IOException {
|
|
196
|
+
if (publicKey.isEmpty()) {
|
|
197
|
+
Log.e(CapacitorUpdater.TAG, "The public key is empty");
|
|
198
|
+
return checksum;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
byte[] checksumBytes = Base64.decode(checksum, Base64.DEFAULT);
|
|
202
|
+
PublicKey pKey = CryptoCipherV2.stringToPublicKey(publicKey);
|
|
203
|
+
byte[] decryptedChecksum = CryptoCipherV2.decryptRSA(checksumBytes, pKey);
|
|
204
|
+
// return Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
205
|
+
String result = Base64.encodeToString(decryptedChecksum, Base64.DEFAULT);
|
|
206
|
+
return result.replaceAll("\\s", ""); // Remove all whitespace, including newlines
|
|
207
|
+
} catch (GeneralSecurityException e) {
|
|
208
|
+
Log.e(CapacitorUpdater.TAG, "decryptChecksum fail: " + e.getMessage());
|
|
209
|
+
throw new IOException("Decryption failed: " + e.getMessage());
|
|
210
|
+
}
|
|
255
211
|
}
|
|
256
212
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
String hex = Integer.toHexString(0xff & b);
|
|
267
|
-
if (hex.length() == 1) hexString.append('0');
|
|
268
|
-
hexString.append(hex);
|
|
269
|
-
}
|
|
270
|
-
return hexString.toString();
|
|
271
|
-
} catch (IOException e) {
|
|
272
|
-
System.err.println(
|
|
273
|
-
CapacitorUpdater.TAG +
|
|
274
|
-
" Cannot calc checksum v2: " +
|
|
275
|
-
file.getPath() +
|
|
276
|
-
" " +
|
|
277
|
-
e.getMessage()
|
|
278
|
-
);
|
|
279
|
-
return "";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
213
|
+
public static String calcChecksum(File file) {
|
|
214
|
+
final int BUFFER_SIZE = 1024 * 1024 * 5; // 5 MB buffer size
|
|
215
|
+
MessageDigest digest;
|
|
216
|
+
try {
|
|
217
|
+
digest = MessageDigest.getInstance("SHA-256");
|
|
218
|
+
} catch (java.security.NoSuchAlgorithmException e) {
|
|
219
|
+
System.err.println(CapacitorUpdater.TAG + " SHA-256 algorithm not available");
|
|
220
|
+
return "";
|
|
221
|
+
}
|
|
282
222
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
223
|
+
try (FileInputStream fis = new FileInputStream(file)) {
|
|
224
|
+
byte[] buffer = new byte[BUFFER_SIZE];
|
|
225
|
+
int length;
|
|
226
|
+
while ((length = fis.read(buffer)) != -1) {
|
|
227
|
+
digest.update(buffer, 0, length);
|
|
228
|
+
}
|
|
229
|
+
byte[] hash = digest.digest();
|
|
230
|
+
StringBuilder hexString = new StringBuilder();
|
|
231
|
+
for (byte b : hash) {
|
|
232
|
+
String hex = Integer.toHexString(0xff & b);
|
|
233
|
+
if (hex.length() == 1) hexString.append('0');
|
|
234
|
+
hexString.append(hex);
|
|
235
|
+
}
|
|
236
|
+
return hexString.toString();
|
|
237
|
+
} catch (IOException e) {
|
|
238
|
+
System.err.println(CapacitorUpdater.TAG + " Cannot calc checksum v2: " + file.getPath() + " " + e.getMessage());
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
288
241
|
}
|
|
289
242
|
|
|
290
|
-
byte[]
|
|
243
|
+
private static byte[] createDEREncoding(int tag, byte[] value) {
|
|
244
|
+
if (tag < 0 || tag >= 0xFF) {
|
|
245
|
+
throw new IllegalArgumentException("Currently only single byte tags supported");
|
|
246
|
+
}
|
|
291
247
|
|
|
292
|
-
|
|
293
|
-
byte[] derEncodingBuf = new byte[size];
|
|
248
|
+
byte[] lengthEncoding = createDERLengthEncoding(value.length);
|
|
294
249
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
System.arraycopy(
|
|
298
|
-
lengthEncoding,
|
|
299
|
-
0,
|
|
300
|
-
derEncodingBuf,
|
|
301
|
-
off,
|
|
302
|
-
lengthEncoding.length
|
|
303
|
-
);
|
|
304
|
-
off += lengthEncoding.length;
|
|
305
|
-
System.arraycopy(value, 0, derEncodingBuf, off, value.length);
|
|
250
|
+
int size = 1 + lengthEncoding.length + value.length;
|
|
251
|
+
byte[] derEncodingBuf = new byte[size];
|
|
306
252
|
|
|
307
|
-
|
|
308
|
-
|
|
253
|
+
int off = 0;
|
|
254
|
+
derEncodingBuf[off++] = (byte) tag;
|
|
255
|
+
System.arraycopy(lengthEncoding, 0, derEncodingBuf, off, lengthEncoding.length);
|
|
256
|
+
off += lengthEncoding.length;
|
|
257
|
+
System.arraycopy(value, 0, derEncodingBuf, off, value.length);
|
|
309
258
|
|
|
310
|
-
|
|
311
|
-
if (size <= 0x7F) {
|
|
312
|
-
// single byte length encoding
|
|
313
|
-
return new byte[] { (byte) size };
|
|
314
|
-
} else if (size <= 0xFF) {
|
|
315
|
-
// double byte length encoding
|
|
316
|
-
return new byte[] { (byte) 0x81, (byte) size };
|
|
317
|
-
} else if (size <= 0xFFFF) {
|
|
318
|
-
// triple byte length encoding
|
|
319
|
-
return new byte[] {
|
|
320
|
-
(byte) 0x82,
|
|
321
|
-
(byte) (size >> Byte.SIZE),
|
|
322
|
-
(byte) size,
|
|
323
|
-
};
|
|
259
|
+
return derEncodingBuf;
|
|
324
260
|
}
|
|
325
261
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
262
|
+
private static byte[] createDERLengthEncoding(int size) {
|
|
263
|
+
if (size <= 0x7F) {
|
|
264
|
+
// single byte length encoding
|
|
265
|
+
return new byte[] { (byte) size };
|
|
266
|
+
} else if (size <= 0xFF) {
|
|
267
|
+
// double byte length encoding
|
|
268
|
+
return new byte[] { (byte) 0x81, (byte) size };
|
|
269
|
+
} else if (size <= 0xFFFF) {
|
|
270
|
+
// triple byte length encoding
|
|
271
|
+
return new byte[] { (byte) 0x82, (byte) (size >> Byte.SIZE), (byte) size };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
|
|
275
|
+
}
|
|
330
276
|
}
|
|
@@ -4,25 +4,25 @@ import org.json.JSONArray;
|
|
|
4
4
|
|
|
5
5
|
public class DataManager {
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
private static DataManager instance;
|
|
8
|
+
private JSONArray currentManifest;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
private DataManager() {}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
public static synchronized DataManager getInstance() {
|
|
13
|
+
if (instance == null) {
|
|
14
|
+
instance = new DataManager();
|
|
15
|
+
}
|
|
16
|
+
return instance;
|
|
15
17
|
}
|
|
16
|
-
return instance;
|
|
17
|
-
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
public void setManifest(JSONArray manifest) {
|
|
20
|
+
this.currentManifest = manifest;
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
public JSONArray getAndClearManifest() {
|
|
24
|
+
JSONArray manifest = this.currentManifest;
|
|
25
|
+
this.currentManifest = null;
|
|
26
|
+
return manifest;
|
|
27
|
+
}
|
|
28
28
|
}
|
|
@@ -12,52 +12,48 @@ import java.util.Objects;
|
|
|
12
12
|
|
|
13
13
|
public class DelayCondition {
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
"DelayCondition{" + "kind=" + kind + ", value='" + value + '\'' + '}'
|
|
61
|
-
);
|
|
62
|
-
}
|
|
15
|
+
@SerializedName("kind")
|
|
16
|
+
private DelayUntilNext kind;
|
|
17
|
+
|
|
18
|
+
@SerializedName("value")
|
|
19
|
+
private String value;
|
|
20
|
+
|
|
21
|
+
public DelayCondition(DelayUntilNext kind, String value) {
|
|
22
|
+
this.kind = kind;
|
|
23
|
+
this.value = value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public DelayUntilNext getKind() {
|
|
27
|
+
return kind;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public void setKind(DelayUntilNext kind) {
|
|
31
|
+
this.kind = kind;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public String getValue() {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public void setValue(String value) {
|
|
39
|
+
this.value = value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Override
|
|
43
|
+
public boolean equals(Object o) {
|
|
44
|
+
if (this == o) return true;
|
|
45
|
+
if (!(o instanceof DelayCondition that)) return false;
|
|
46
|
+
return (getKind() == that.getKind() && Objects.equals(getValue(), that.getValue()));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Override
|
|
50
|
+
public int hashCode() {
|
|
51
|
+
return Objects.hash(getKind(), getValue());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@NonNull
|
|
55
|
+
@Override
|
|
56
|
+
public String toString() {
|
|
57
|
+
return ("DelayCondition{" + "kind=" + kind + ", value='" + value + '\'' + '}');
|
|
58
|
+
}
|
|
63
59
|
}
|