Implemented nameio/stream file name encryption algorithm

This change adds support for nameio/stream mode of filename encryption.
Also as a bonus change it fixes the MAC computation for volumes that
do not use chained name IV.
This commit is contained in:
Mark Pariente 2011-12-16 01:41:40 -08:00
parent 10e86483c6
commit 1fb202421f
4 changed files with 127 additions and 20 deletions

View file

@ -20,6 +20,16 @@ package org.mrpdaemon.sec.encfs;
*/
public class EncFSConfig {
/**
* Volume configuration uses nameio/block for filename encryption
*/
public static final int ENCFS_CONFIG_NAME_ALG_BLOCK = 1;
/**
* Volume configuration uses nameio/stream for filename encryption
*/
public static final int ENCFS_CONFIG_NAME_ALG_STREAM = 2;
// Size of the volume encryption key in bits.
private int volumeKeySize;
@ -60,6 +70,9 @@ public class EncFSConfig {
// Iteration count used in the generation of the password derived key.
private int iterationCount;
// Algorithm used for file name encryption
private int nameAlgorithm;
/**
* @return the size of the volume encryption key in bits.
*/
@ -212,4 +225,18 @@ public class EncFSConfig {
public void setIterationCount(int iterationCount) {
this.iterationCount = iterationCount;
}
/**
* @return algorithm used for filename encryption
*/
public int getNameAlgorithm() {
return nameAlgorithm;
}
/**
* @param nameAlgorithm algorithm used for filename encryption
*/
public void setNameAlgorithm(int nameAlgorithm) {
this.nameAlgorithm = nameAlgorithm;
}
}

View file

@ -72,7 +72,26 @@ public class EncFSConfigParser {
Node cfgNode = cfgNodeList.item(i);
if (cfgNode.getNodeType() == Node.ELEMENT_NODE) {
if (cfgNode.getNodeName().equals("keySize")) {
if (cfgNode.getNodeName().equals("nameAlg")) {
NodeList nameAlgNodeList = cfgNode.getChildNodes();
for (int j = 0; j < nameAlgNodeList.getLength(); j++) {
Node nameAlgChildNode = nameAlgNodeList.item(j);
if (nameAlgChildNode.getNodeName().equals("name")) {
String algName = getNodeValue(nameAlgChildNode);
if (algName.equals("nameio/block")) {
config.setNameAlgorithm(
EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK);
} else if (algName.equals("nameio/stream")) {
config.setNameAlgorithm(
EncFSConfig.ENCFS_CONFIG_NAME_ALG_STREAM);
} else {
throw new EncFSInvalidConfigException(
"Unknown name algorithm in config file: " +
algName);
}
}
}
} else if (cfgNode.getNodeName().equals("keySize")) {
config.setVolumeKeySize(Integer.parseInt(
getNodeValue(cfgNode)));
} else if (cfgNode.getNodeName().equals("blockSize")) {

View file

@ -129,6 +129,36 @@ public class EncFSCrypto {
volume.getIV(), ivSeed);
}
public static byte[] mac64(Mac mac, byte[] input) {
byte[] macResult = mac.doFinal(input);
byte[] mac64 = new byte[8];
for (int i = 0; i < 19; i++) // Note the 19 not 20
mac64[i % 8] ^= macResult[i];
return mac64;
}
public static byte[] mac32(Mac mac, byte[] input) {
byte[] mac64 = mac64(mac, input);
byte[] mac32 = new byte[4];
mac32[0] = (byte) (mac64[4] ^ mac64[0]);
mac32[1] = (byte) (mac64[5] ^ mac64[1]);
mac32[2] = (byte) (mac64[6] ^ mac64[2]);
mac32[3] = (byte) (mac64[7] ^ mac64[3]);
return mac32;
}
public static byte[] mac16(Mac mac, byte[] input) {
byte[] mac32 = mac32(mac, input);
byte[] mac16 = new byte[2];
mac16[0] = (byte) (mac32[2] ^ mac32[0]);
mac16[1] = (byte) (mac32[3] ^ mac32[1]);
return mac16;
}
public static byte[] mac64(Mac mac, byte[] input, byte[] chainedIv) {
byte[] concat = new byte[input.length + chainedIv.length];
for (int i = 0; i < input.length; i++) {
@ -197,7 +227,7 @@ public class EncFSCrypto {
return result;
}
private static byte[] streamDecode(Cipher cipher, Mac mac, Key key,
public static byte[] streamDecode(Cipher cipher, Mac mac, Key key,
byte[] iv, byte[] ivSeed, byte[] data)
throws EncFSUnsupportedException,
InvalidAlgorithmParameterException,

View file

@ -149,32 +149,63 @@ public class EncFSFile {
fileIv[i] = (byte) (macBytes[i] ^ chainIv[i]);
}
// Decrypt filename
Cipher blockCipher= volume.getBlockCipher();
try {
EncFSCrypto.cipherInit(volume, Cipher.DECRYPT_MODE,
blockCipher, fileIv);
} catch (InvalidAlgorithmParameterException e) {
throw new EncFSCorruptDataException(e.getMessage());
}
Cipher cipher;
byte[] decFileName;
try {
decFileName = blockCipher.doFinal(encFileName);
} catch (IllegalBlockSizeException e) {
throw new EncFSCorruptDataException(e.getMessage());
} catch (BadPaddingException e) {
throw new EncFSCorruptDataException(e.getMessage());
if (volume.getConfig().getNameAlgorithm() ==
EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
//Block decryption
cipher = volume.getBlockCipher();
try {
EncFSCrypto.cipherInit(volume, Cipher.DECRYPT_MODE,
cipher, fileIv);
} catch (InvalidAlgorithmParameterException e) {
throw new EncFSCorruptDataException(e.getMessage());
}
try {
decFileName = cipher.doFinal(encFileName);
} catch (IllegalBlockSizeException e) {
throw new EncFSCorruptDataException(e.getMessage());
} catch (BadPaddingException e) {
throw new EncFSCorruptDataException(e.getMessage());
}
} else {
// Stream decryption
try {
decFileName = EncFSCrypto.streamDecode(volume, fileIv, encFileName);
} catch (InvalidAlgorithmParameterException e) {
throw new EncFSCorruptDataException(e.getMessage());
} catch (IllegalBlockSizeException e) {
throw new EncFSCorruptDataException(e.getMessage());
} catch (BadPaddingException e) {
throw new EncFSCorruptDataException(e.getMessage());
} catch (EncFSUnsupportedException e) {
throw new EncFSCorruptDataException(e.getMessage());
}
}
// Verify decryption worked
byte[] mac16 = EncFSCrypto.mac16(volume.getMac(), decFileName,
chainIv);
byte[] mac16;
if (volume.getConfig().isChainedNameIV()) {
mac16 = EncFSCrypto.mac16(volume.getMac(), decFileName, chainIv);
} else {
mac16 = EncFSCrypto.mac16(volume.getMac(), decFileName);
}
byte[] expectedMac = Arrays.copyOfRange(base256FileName, 0, 2);
if (!Arrays.equals(mac16, expectedMac)) {
throw new EncFSChecksumException("Mismatch in file checksum");
}
// For the stream cipher directly return the result
if (volume.getConfig().getNameAlgorithm() ==
EncFSConfig.ENCFS_CONFIG_NAME_ALG_STREAM) {
return new String(decFileName);
}
// For the block cipher remove padding before returning the result
int padLen = decFileName[decFileName.length - 1];
return new String(Arrays.copyOfRange(decFileName, 0,