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.
etesync-android/app/src/main/java/at/bitfire/davdroid/journalmanager/Helpers.java

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