Cloud sync improvements

pull/212/head
M66B 3 years ago
parent 6651bc0b8e
commit b93b192762

@ -29,18 +29,15 @@ import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.security.InvalidAlgorithmParameterException; import java.nio.ByteBuffer;
import java.security.InvalidKeyException; import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.util.Arrays; import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
@ -52,10 +49,7 @@ public class CloudSync {
private static final int CLOUD_TIMEOUT = 10 * 1000; // timeout private static final int CLOUD_TIMEOUT = 10 * 1000; // timeout
public static JSONObject perform(Context context, String user, String password, JSONObject jrequest) public static JSONObject perform(Context context, String user, String password, JSONObject jrequest)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, throws GeneralSecurityException, JSONException, IOException {
InvalidKeySpecException, InvalidKeyException,
IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException,
JSONException, IOException {
byte[] salt = MessageDigest.getInstance("SHA256").digest(user.getBytes()); byte[] salt = MessageDigest.getInstance("SHA256").digest(user.getBytes());
byte[] huser = MessageDigest.getInstance("SHA256").digest(salt); byte[] huser = MessageDigest.getInstance("SHA256").digest(salt);
byte[] userid = Arrays.copyOfRange(huser, 0, 8); byte[] userid = Arrays.copyOfRange(huser, 0, 8);
@ -69,24 +63,18 @@ public class CloudSync {
jrequest.put("password", cloudPassword); jrequest.put("password", cloudPassword);
jrequest.put("debug", BuildConfig.DEBUG); jrequest.put("debug", BuildConfig.DEBUG);
if (jrequest.has("keys")) {
JSONArray jkeys = jrequest.getJSONArray("keys");
for (int i = 0; i < jkeys.length(); i++) {
jkeys.put(i, transform(jkeys.getString(i), key.second, true));
}
}
if (jrequest.has("items")) { if (jrequest.has("items")) {
JSONArray jitems = jrequest.getJSONArray("items"); JSONArray jitems = jrequest.getJSONArray("items");
for (int i = 0; i < jitems.length(); i++) { for (int i = 0; i < jitems.length(); i++) {
JSONObject jitem = jitems.getJSONObject(i); JSONObject jitem = jitems.getJSONObject(i);
int revision = jitem.getInt("revision");
String k = jitem.getString("key"); String k = jitem.getString("key");
jitem.put("key", transform(k, key.second, true)); jitem.put("key", transform(k, key.second, null, true));
if (jitem.has("value") && !jitem.isNull("value")) { if (jitem.has("value") && !jitem.isNull("value")) {
String v = jitem.getString("value"); String v = jitem.getString("value");
jitem.put("value", transform(v, key.second, true)); jitem.put("value", transform(v, key.second, revision, true));
} }
} }
} }
@ -130,13 +118,14 @@ public class CloudSync {
JSONArray jitems = jresponse.getJSONArray("items"); JSONArray jitems = jresponse.getJSONArray("items");
for (int i = 0; i < jitems.length(); i++) { for (int i = 0; i < jitems.length(); i++) {
JSONObject jitem = jitems.getJSONObject(i); JSONObject jitem = jitems.getJSONObject(i);
int revision = jitem.getInt("revision");
String ekey = jitem.getString("key"); String ekey = jitem.getString("key");
jitem.put("key", transform(ekey, key.second, false)); jitem.put("key", transform(ekey, key.second, null, false));
if (jitem.has("value") && !jitem.isNull("value")) { if (jitem.has("value") && !jitem.isNull("value")) {
String evalue = jitem.getString("value"); String evalue = jitem.getString("value");
jitem.put("value", transform(evalue, key.second, false)); jitem.put("value", transform(evalue, key.second, revision, false));
} }
} }
} }
@ -161,13 +150,13 @@ public class CloudSync {
Arrays.copyOfRange(encoded, half, half + half)); Arrays.copyOfRange(encoded, half, half + half));
} }
private static String transform(String value, byte[] key, boolean encrypt) private static String transform(String value, byte[] key, Integer revision, boolean encrypt) throws GeneralSecurityException {
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
SecretKeySpec secret = new SecretKeySpec(key, "AES"); SecretKeySpec secret = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM-SIV/NoPadding"); Cipher cipher = Cipher.getInstance("AES/GCM-SIV/NoPadding");
IvParameterSpec ivSpec = new IvParameterSpec(new byte[12]); IvParameterSpec ivSpec = new IvParameterSpec(new byte[12]);
cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secret, ivSpec); cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secret, ivSpec);
//cipher.updateAAD(ByteBuffer.allocate(4).putInt(0).array()); if (revision != null)
cipher.updateAAD(ByteBuffer.allocate(4).putInt(revision).array());
if (encrypt) { if (encrypt) {
byte[] encrypted = cipher.doFinal(value.getBytes()); byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeToString(encrypted, Base64.NO_PADDING | Base64.NO_WRAP); return Base64.encodeToString(encrypted, Base64.NO_PADDING | Base64.NO_WRAP);

@ -1532,32 +1532,29 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
JSONObject jrequest = new JSONObject(); JSONObject jrequest = new JSONObject();
jrequest.put("command", wipe ? "wipe" : "login"); jrequest.put("command", wipe ? "wipe" : "login");
if (false) { if (true) {
JSONArray jwrite = new JSONArray(); boolean sync = true;
JSONArray jitems = new JSONArray();
JSONObject jkv0 = new JSONObject(); JSONObject jkv0 = new JSONObject();
jkv0.put("key", "key0"); jkv0.put("key", "key0");
jkv0.put("timestamp", 1000); jkv0.put("revision", 1000);
if (!sync)
jkv0.put("value", "value0"); jkv0.put("value", "value0");
jwrite.put(jkv0); jitems.put(jkv0);
JSONObject jkv1 = new JSONObject(); JSONObject jkv1 = new JSONObject();
jkv1.put("key", "key1"); jkv1.put("key", "key1");
jkv1.put("timestamp", 1001); jkv1.put("revision", 1001);
if (!sync)
jkv1.put("value", "value1"); jkv1.put("value", "value1");
jwrite.put(jkv1); jitems.put(jkv1);
jrequest.put("command", "write"); jrequest.put("command", sync ? "read" : "write");
jrequest.put("stage", "ack"); if (sync)
jrequest.put("items", jwrite); jrequest.put("compare", true);
} jrequest.put("items", jitems);
if (true) {
JSONArray jread = new JSONArray();
jread.put("key1");
jrequest.put("command", "read");
jrequest.put("stage", "sync");
jrequest.put("keys", jread);
} }
JSONObject jresponse = CloudSync.perform(context, user, password, jrequest); JSONObject jresponse = CloudSync.perform(context, user, password, jrequest);
@ -1567,9 +1564,9 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
for (int i = 0; i < jitems.length(); i++) { for (int i = 0; i < jitems.length(); i++) {
JSONObject jitem = jitems.getJSONObject(i); JSONObject jitem = jitems.getJSONObject(i);
String key = jitem.getString("key"); String key = jitem.getString("key");
long timestamp = jitem.getLong("timestamp"); long revision = jitem.getLong("revision");
String value = (jitem.has("value") ? jitem.getString("value") : null); String value = (jitem.has("value") ? jitem.getString("value") : null);
Log.i("Cloud item " + key + "=" + value + " @" + timestamp); Log.i("Cloud item " + key + "=" + value + " @" + revision);
} }
} }
return jresponse.optString("status"); return jresponse.optString("status");

Loading…
Cancel
Save