/*
* EncFS Java Library
* Copyright (C) 2013 encfs-java authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
package org.mrpdaemon.sec.encfs;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.util.Arrays;
import java.util.StringTokenizer;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
/**
* Static methods for stream cryptography.
*/
public final class StreamCrypto {
private StreamCrypto() {
}
/**
* Returns a stream cipher.
*/
public static Cipher newStreamCipher() throws EncFSUnsupportedException {
return EncFSCrypto.getCipher(EncFSCrypto.STREAM_CIPHER);
}
/**
* Stream decryption implementation.
*/
private static byte[] streamDecrypt(Cipher cipher, Mac mac, Key key,
byte[] iv, byte[] ivSeed, byte[] data, int offset, int len)
throws EncFSUnsupportedException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
// First round uses IV seed + 1 for IV generation
byte[] ivSeedPlusOne = EncFSCrypto.incrementIvSeedByOne(ivSeed);
EncFSCrypto.cipherInit(key, mac, Cipher.DECRYPT_MODE, cipher, iv,
ivSeedPlusOne);
byte[] firstDecResult = cipher.doFinal(data, offset, len);
EncFSCrypto.unshuffleBytes(firstDecResult);
byte[] flipBytesResult = EncFSCrypto.flipBytes(firstDecResult);
// Second round of decryption with IV seed itself used for IV generation
EncFSCrypto.cipherInit(key, mac, Cipher.DECRYPT_MODE, cipher, iv,
ivSeed);
byte[] result = cipher.doFinal(flipBytesResult);
EncFSCrypto.unshuffleBytes(result);
return result;
}
/**
* Stream decryption implementation.
*/
static byte[] streamDecrypt(Cipher cipher, Mac mac, Key key, byte[] iv,
byte[] ivSeed, byte[] data) throws EncFSUnsupportedException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
return streamDecrypt(cipher, mac, key, iv, ivSeed, data, 0, data.length);
}
/**
* Stream decryption implementation.
*/
public static byte[] streamDecrypt(EncFSVolume volume, byte[] ivSeed, byte[] data)
throws EncFSUnsupportedException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
Cipher streamCipher = volume.getStreamCipher();
return streamDecrypt(streamCipher, volume.getMAC(), volume.getKey(),
volume.getIV(), ivSeed, data);
}
/**
* Stream decryption implementation.
*/
public static byte[] streamDecrypt(EncFSVolume volume, byte[] ivSeed, byte[] data, int offset, int len)
throws EncFSUnsupportedException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
return streamDecrypt(volume.getStreamCipher(), volume.getMAC(),
volume.getKey(), volume.getIV(), ivSeed, data, offset, len);
}
/**
* Stream encryption implementation.
*/
private static byte[] streamEncrypt(Cipher cipher, Mac mac, Key key, byte[] iv, byte[] ivSeed, byte[] data, int offset, int len)
throws EncFSUnsupportedException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// First round uses IV seed + 1 for IV generation
byte[] ivSeedPlusOne = EncFSCrypto.incrementIvSeedByOne(ivSeed);
byte[] encBuf = Arrays.copyOfRange(data, offset, offset+len);
EncFSCrypto.shuffleBytes(encBuf);
EncFSCrypto.cipherInit(key, mac, Cipher.ENCRYPT_MODE, cipher, iv, ivSeed);
byte[] firstEncResult = cipher.doFinal(encBuf);
byte[] flipBytesResult = EncFSCrypto.flipBytes(firstEncResult);
EncFSCrypto.shuffleBytes(flipBytesResult);
// Second round of encryption with IV seed itself used for IV generation
EncFSCrypto.cipherInit(key, mac, Cipher.ENCRYPT_MODE, cipher, iv, ivSeedPlusOne);
return cipher.doFinal(flipBytesResult);
}
/**
* Stream encryption implementation.
*/
static byte[] streamEncrypt(Cipher cipher, Mac mac, Key key, byte[] iv, byte[] ivSeed, byte[] data) throws EncFSUnsupportedException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
return streamEncrypt(cipher, mac, key, iv, ivSeed, data, 0, data.length);
}
/**
* Stream encryption implementation.
*/
public static byte[] streamEncrypt(EncFSVolume volume, byte[] ivSeed, byte[] data)
throws EncFSUnsupportedException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
return streamEncrypt(volume.getStreamCipher(), volume.getMAC(),
volume.getKey(), volume.getIV(), ivSeed, data);
}
/**
* Stream encryption implementation.
*/
public static byte[] streamEncrypt(EncFSVolume volume, byte[] ivSeed, byte[] data, int offset, int len)
throws EncFSUnsupportedException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
return streamEncrypt(volume.getStreamCipher(), volume.getMAC(),
volume.getKey(), volume.getIV(), ivSeed, data, offset, len);
}
/**
* Compute chain IV for the given volume path
*
* @param volume
* Volume to compute chain IV for
* @param volumePath
* Volume path to compute chain IV for
* @return Computed chain IV
*/
public static byte[] computeChainIv(EncFSVolume volume, String volumePath) {
byte[] chainIv = new byte[8];
StringTokenizer st = new StringTokenizer(volumePath,
EncFSVolume.PATH_SEPARATOR);
while (st.hasMoreTokens()) {
String curPath = st.nextToken();
if ((curPath.length()>0)
&&(!curPath.equals(EncFSVolume.PATH_SEPARATOR))) {
byte[] encodeBytes;
if (volume.getConfig().getFilenameAlgorithm()==EncFSFilenameEncryptionAlgorithm.BLOCK) {
encodeBytes = EncFSCrypto
.getBytesForBlockAlgorithm(curPath);
} else {
encodeBytes = curPath.getBytes();
}
// Update chain IV
EncFSCrypto.mac64(volume.getMAC(), encodeBytes, chainIv);
}
}
return chainIv;
}
}