/*
* Copyright (C) 2010-2015, Martin Goellnitz
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA
*/
package jfs.sync.encrypted;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.HashSet;
import jfs.sync.encryption.AbstractEncryptedStorageAccess;
import jfs.sync.encryption.FileInfo;
import jfs.sync.encryption.StorageAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a storage layer access which encryptes the filenames, compresses and encrytes contents and is aware of the
* meta solution in the other package. To be able to deal with the original file length, every file has a header
* containing information about compression and original length.
*
* It has been refactored from an older version to meet higher security standards concerning known plain text attacks
* and re-use of encryption keys
*/
public class EncryptedFileStorageAccess extends AbstractEncryptedStorageAccess implements StorageAccess {
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFileStorageAccess.class);
private final String cipherspec;
public EncryptedFileStorageAccess(String cipher, boolean shortenPaths) {
super(shortenPaths);
cipherspec = cipher;
} // EncryptedFileStorageAccess()
@Override
public String getSeparator() {
return File.separator;
}
@Override
public String getCipherSpec() {
return cipherspec;
} // getCipherSpec()
protected File getFile(String rootPath, String relativePath) {
String path = getFileName(relativePath);
return new File(rootPath+path);
} // getFile()
@Override
public String[] list(String rootPath, String relativePath) {
String[] items = getFile(rootPath, relativePath).list();
// decrypt
String[] result = new String[items.length];
int i = 0;
for (String item : items) {
String decryptedItem = getDecryptedFileName(relativePath, item);
result[i++] = decryptedItem;
LOG.info("list() {} -> {}", item, decryptedItem);
} // for
// sort out meta data
Collection<String> itemCollection = new HashSet<>();
for (String item : result) {
if (!getMetaDataFileName(relativePath).equals(item)) {
itemCollection.add(item);
} // if
} // for
// repackage as array
result = new String[itemCollection.size()];
i = 0;
for (String item : itemCollection) {
result[i++] = item;
} // for
return result;
} // list()
@Override
public FileInfo getFileInfo(String rootPath, String relativePath) {
FileInfo result = new FileInfo();
String name = getLastPathElement(relativePath, relativePath);
result.setName(name);
result.setPath(rootPath+relativePath);
File file = getFile(rootPath, relativePath);
result.setDirectory(file.isDirectory());
result.setExists(file.exists());
result.setCanRead(true);
result.setCanWrite(true);
if (LOG.isDebugEnabled()) {
LOG.debug("getFileInfo() "+result.getPath()+" e["+result.isExists()+"] d["+result.isDirectory()+"]");
} // if
if (result.isExists()) {
result.setCanRead(file.canRead());
result.setCanWrite(file.canWrite());
if (!result.isDirectory()) {
result.setModificationDate(file.lastModified());
result.setSize(-1);
} else {
result.setSize(0);
} // if
} else {
LOG.debug("getFileInfo() could not detect file for {}", result.getPath());
} // if
return result;
} // getFileInfo()
@Override
public boolean createDirectory(String rootPath, String relativePath) {
LOG.debug("createDirectory() {}", relativePath);
return getFile(rootPath, relativePath).mkdir();
}
@Override
public boolean setLastModified(String rootPath, String relativePath, long modificationDate) {
return getFile(rootPath, relativePath).setLastModified(modificationDate);
}
@Override
public boolean setReadOnly(String rootPath, String relativePath) {
return getFile(rootPath, relativePath).setReadOnly();
}
protected String filePermissionsString(File file) {
return (file.canRead() ? "r" : "-")+(file.canWrite() ? "w" : "-")+(file.canExecute() ? "x" : "-");
} // filePermissionsString()
@Override
public boolean delete(String rootPath, String relativePath) {
File file = getFile(rootPath, relativePath);
LOG.warn("delete({}) {}", relativePath, file.getAbsolutePath());
LOG.warn("delete({}) file.exists(): {} {}", relativePath, file.exists(), filePermissionsString(file));
if (file.isDirectory()) {
String metaDataPath = getMetaDataPath(relativePath);
File metaDataFile = getFile(rootPath, metaDataPath);
if (metaDataFile.exists()) {
metaDataFile.delete();
} // if
} // if
file.delete();
LOG.warn("delete({}) file.exists(): {}", relativePath, file.exists());
return !file.exists();
} // delete()
@Override
public InputStream getInputStream(String rootPath, String relativePath) throws IOException {
File file = getFile(rootPath, relativePath);
LOG.debug("getInputStream() getting input stream for {}", file.getPath());
return new FileInputStream(file);
}
@Override
public OutputStream getOutputStream(String rootPath, String relativePath) throws IOException {
File file = getFile(rootPath, relativePath);
LOG.debug("getOutputStream() getting output stream for {}", file.getPath());
return new FileOutputStream(file);
}
@Override
public void flush(String rootPath, FileInfo info) {
// Nothing to do in this implementation
} // flush()
/**
* Test
*/
public static void main(String[] args) throws Exception {
EncryptedFileStorageAccess d = new EncryptedFileStorageAccess("AES", true);
String relativePath = "a/path/for/me";
String enc = d.getEncryptedFileName(relativePath, "src");
System.out.println("enc= "+enc);
String plain = d.getDecryptedFileName(relativePath, enc);
System.out.println("plain= "+plain);
d.getPassword(relativePath);
} // main()
} // EncryptedFileStorageAccess