1
0
mirror of https://github.com/etesync/android synced 2025-06-30 20:02:41 +00:00
etesync-android/app/src/main/java/at/bitfire/davdroid/journalmanager/Helpers.java
Tom Hacohen 8b5f87c2d4 Adjust DAVdroid to use the EteSync protocol (mostly working)
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.
2017-02-21 17:26:19 +00:00

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);
}
}
}