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:
parent
10e86483c6
commit
1fb202421f
4 changed files with 127 additions and 20 deletions
src/main/java/org/mrpdaemon/sec/encfs
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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")) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue