Cloud sync: native BouncyCastle

pull/212/head
M66B 2 years ago
parent 6087ae32f5
commit 038cd2514f

@ -27,6 +27,11 @@ import android.util.Pair;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.GCMSIVBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -53,12 +58,9 @@ import java.util.Map;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import javax.crypto.Cipher;
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.PBEKeySpec; import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
public class CloudSync { public class CloudSync {
@ -70,7 +72,7 @@ public class CloudSync {
// Upper level // Upper level
static void execute(Context context, String command, boolean manual) static void execute(Context context, String command, boolean manual)
throws JSONException, GeneralSecurityException, IOException { throws JSONException, GeneralSecurityException, IOException, InvalidCipherTextException {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String user = prefs.getString("cloud_user", null); String user = prefs.getString("cloud_user", null);
String password = prefs.getString("cloud_password", null); String password = prefs.getString("cloud_password", null);
@ -220,7 +222,7 @@ public class CloudSync {
} }
private static void sendLocalData(Context context, String user, String password, long lrevision) private static void sendLocalData(Context context, String user, String password, long lrevision)
throws JSONException, GeneralSecurityException, IOException { throws JSONException, GeneralSecurityException, IOException, InvalidCipherTextException {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean cloud_send = prefs.getBoolean("cloud_send", true); boolean cloud_send = prefs.getBoolean("cloud_send", true);
@ -309,7 +311,7 @@ public class CloudSync {
} }
private static void receiveRemoteData(Context context, String user, String password, long lrevision, long rrevision, JSONObject jstatus) private static void receiveRemoteData(Context context, String user, String password, long lrevision, long rrevision, JSONObject jstatus)
throws JSONException, GeneralSecurityException, IOException { throws JSONException, GeneralSecurityException, IOException, InvalidCipherTextException {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
File dir = Helper.ensureExists(new File(context.getFilesDir(), "syncdata")); File dir = Helper.ensureExists(new File(context.getFilesDir(), "syncdata"));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
@ -534,7 +536,7 @@ public class CloudSync {
// Lower level // Lower level
public static JSONObject call(Context context, String user, String password, String command, JSONObject jrequest) public static JSONObject call(Context context, String user, String password, String command, JSONObject jrequest)
throws GeneralSecurityException, JSONException, IOException { throws GeneralSecurityException, JSONException, IOException, InvalidCipherTextException {
EntityLog.log(context, EntityLog.Type.Cloud, "Cloud command=" + command); EntityLog.log(context, EntityLog.Type.Cloud, "Cloud command=" + command);
jrequest.put("command", command); jrequest.put("command", command);
@ -559,7 +561,7 @@ public class CloudSync {
} }
private static JSONObject _call(Context context, String user, String password, JSONObject jrequest) private static JSONObject _call(Context context, String user, String password, JSONObject jrequest)
throws GeneralSecurityException, JSONException, IOException { throws GeneralSecurityException, JSONException, IOException, InvalidCipherTextException {
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);
@ -710,21 +712,25 @@ public class CloudSync {
} }
private static String transform(String value, byte[] key, byte[] ad, boolean encrypt) private static String transform(String value, byte[] key, byte[] ad, boolean encrypt)
throws GeneralSecurityException, IOException { throws InvalidCipherTextException, IOException {
SecretKeySpec secret = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM-SIV/NoPadding"); byte[] iv = new byte[12];
IvParameterSpec ivSpec = new IvParameterSpec(new byte[12]); GCMSIVBlockCipher cipher = new GCMSIVBlockCipher(new AESEngine());
cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secret, ivSpec); AEADParameters aead = new AEADParameters(new KeyParameter(key), 128, iv, ad);
if (ad != null) cipher.init(encrypt, aead);
cipher.updateAAD(ad);
if (encrypt) { byte[] input = (encrypt
byte[] encrypted = cipher.doFinal(compress(value.getBytes())); ? compress(value.getBytes())
return Base64.encodeToString(encrypted, Base64.NO_PADDING | Base64.NO_WRAP); : Base64.decode(value, Base64.NO_PADDING | Base64.NO_WRAP));
} else { int outputLength = cipher.getOutputSize(input.length);
byte[] encrypted = Base64.decode(value, Base64.NO_PADDING | Base64.NO_WRAP); byte[] output = new byte[outputLength];
byte[] decrypted = cipher.doFinal(encrypted); int outputOffset = cipher.processBytes(input, 0, input.length, output, 0);
return new String(decompress(decrypted)); outputOffset += cipher.doFinal(output, outputOffset); // tag
} if (outputOffset != outputLength)
throw new IllegalStateException("offset=" + outputOffset + "/" + outputLength);
return (encrypt
? Base64.encodeToString(output, Base64.NO_PADDING | Base64.NO_WRAP)
: new String(decompress(output)));
} }
public static byte[] compress(byte[] data) throws IOException { public static byte[] compress(byte[] data) throws IOException {

Loading…
Cancel
Save