mirror of https://github.com/etesync/android
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
4.6 KiB
129 lines
4.6 KiB
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);
|
|
}
|
|
}
|
|
}
|