mirror of
https://github.com/etesync/android
synced 2025-06-30 20:02:41 +00:00

This commit includes the major changes between DAVdroid and EteSync. It adjusts the app to use the EteSync protocol and server. It includes some ugliness still, and it's a squash of many ugly snapshot commits while hacking on the initial DAVdroid code. History should be "clean" from this point onwards.
129 lines
4.6 KiB
Java
129 lines
4.6 KiB
Java
package at.bitfire.davdroid.journalmanager;
|
|
|
|
import android.util.Base64;
|
|
|
|
import org.apache.commons.codec.Charsets;
|
|
import org.spongycastle.crypto.BufferedBlockCipher;
|
|
import org.spongycastle.crypto.CipherParameters;
|
|
import org.spongycastle.crypto.InvalidCipherTextException;
|
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
import org.spongycastle.crypto.engines.AESEngine;
|
|
import org.spongycastle.crypto.generators.SCrypt;
|
|
import org.spongycastle.crypto.macs.HMac;
|
|
import org.spongycastle.crypto.modes.CBCBlockCipher;
|
|
import org.spongycastle.crypto.paddings.BlockCipherPadding;
|
|
import org.spongycastle.crypto.paddings.PKCS7Padding;
|
|
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
|
import org.spongycastle.crypto.params.KeyParameter;
|
|
import org.spongycastle.crypto.params.ParametersWithIV;
|
|
import org.spongycastle.util.encoders.Hex;
|
|
|
|
import java.security.MessageDigest;
|
|
import java.security.SecureRandom;
|
|
import java.util.Arrays;
|
|
|
|
public class Helpers {
|
|
// FIXME: This should be somewhere else
|
|
public static String deriveKey(String salt, String password) {
|
|
final int keySize = 190;
|
|
|
|
return Base64.encodeToString(SCrypt.generate(password.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8), 16384, 8, 1, keySize), Base64.NO_WRAP);
|
|
}
|
|
|
|
private static byte[] hmac256(byte[] keyByte, byte[] data) {
|
|
HMac hmac = new HMac(new SHA256Digest());
|
|
KeyParameter key = new KeyParameter(keyByte);
|
|
byte[] ret = new byte[hmac.getMacSize()];
|
|
hmac.init(key);
|
|
hmac.update(data, 0, data.length);
|
|
hmac.doFinal(ret, 0);
|
|
return ret;
|
|
}
|
|
|
|
static byte[] hmac(String keyBase64, byte[] data) {
|
|
byte[] derivedKey = hmac256("hmac".getBytes(Charsets.UTF_8), Base64.decode(keyBase64, Base64.NO_WRAP));
|
|
return hmac256(derivedKey, data);
|
|
}
|
|
|
|
static class Cipher {
|
|
SecureRandom random;
|
|
|
|
Cipher() {
|
|
random = new SecureRandom();
|
|
}
|
|
|
|
private static final int blockSize = 16; // AES's block size in bytes
|
|
|
|
private BufferedBlockCipher getCipher(String keyBase64, byte[] iv, boolean encrypt) {
|
|
byte[] derivedKey = hmac256("aes".getBytes(Charsets.UTF_8), Base64.decode(keyBase64, Base64.NO_WRAP));
|
|
KeyParameter key = new KeyParameter(derivedKey);
|
|
CipherParameters params = new ParametersWithIV(key, iv);
|
|
|
|
BlockCipherPadding padding = new PKCS7Padding();
|
|
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
|
|
new CBCBlockCipher(new AESEngine()), padding);
|
|
cipher.reset();
|
|
cipher.init(encrypt, params);
|
|
|
|
return cipher;
|
|
}
|
|
|
|
byte[] decrypt(String keyBase64, byte[] _data) {
|
|
byte[] iv = Arrays.copyOfRange(_data, 0, blockSize);
|
|
byte[] data = Arrays.copyOfRange(_data, blockSize, _data.length);
|
|
|
|
BufferedBlockCipher cipher = getCipher(keyBase64, iv, false);
|
|
|
|
byte[] buf = new byte[cipher.getOutputSize(data.length)];
|
|
int len = cipher.processBytes(data, 0, data.length, buf, 0);
|
|
try {
|
|
len += cipher.doFinal(buf, len);
|
|
} catch (InvalidCipherTextException e) {
|
|
// FIXME: Fail!
|
|
e.printStackTrace();
|
|
}
|
|
|
|
// remove padding
|
|
byte[] out = new byte[len];
|
|
System.arraycopy(buf, 0, out, 0, len);
|
|
|
|
return out;
|
|
}
|
|
|
|
byte[] encrypt(String keyBase64, byte[] data) {
|
|
byte[] iv = new byte[blockSize];
|
|
random.nextBytes(iv);
|
|
|
|
BufferedBlockCipher cipher = getCipher(keyBase64, iv, true);
|
|
|
|
byte[] buf = new byte[cipher.getOutputSize(data.length) + blockSize];
|
|
System.arraycopy(iv, 0, buf, 0, iv.length);
|
|
int len = iv.length + cipher.processBytes(data, 0, data.length, buf, iv.length);
|
|
try {
|
|
cipher.doFinal(buf, len);
|
|
} catch (InvalidCipherTextException e) {
|
|
// FIXME: Fail!
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Replace with bouncy-castle once available. */
|
|
static String sha256(String base) {
|
|
return sha256(base.getBytes(Charsets.UTF_8));
|
|
}
|
|
|
|
/* FIXME: Replace with bouncy-castle once available. */
|
|
static String sha256(byte[] base) {
|
|
try {
|
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
byte[] hash = digest.digest(base);
|
|
return Hex.toHexString(hash);
|
|
} catch (Exception ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
}
|