/* * * Panbox - encryption for cloud storage * Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG * * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Additonally, third party code may be provided with notices and open source * licenses from communities and third parties that govern the use of those * portions, and any licenses granted hereunder do not alter any rights and * obligations you may have under such open source licenses, however, the * disclaimer of warranty and limitation of liability provisions of the GPLv3 * will apply to all the product. * */ package org.panbox.linux.desktop.vfs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.List; import javax.crypto.SecretKey; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.panbox.core.crypto.AbstractObfuscatorFactory; import org.panbox.core.crypto.CryptCore; import org.panbox.core.crypto.FileObfuscatorFactory; import org.panbox.core.crypto.Obfuscator; import org.panbox.core.crypto.io.AESGCMRandomAccessFile; import org.panbox.core.exception.ObfuscationException; import org.panbox.core.exception.ShareMetaDataException; import org.panbox.core.exception.SymmetricKeyDecryptionException; import org.panbox.core.exception.SymmetricKeyNotFoundException; import org.panbox.core.identitymgmt.Identity; import org.panbox.core.identitymgmt.SimpleAddressbook; import org.panbox.core.keymgmt.EncryptedShareKey; import org.panbox.core.keymgmt.ShareKey; import org.panbox.core.keymgmt.Volume; import org.panbox.core.keymgmt.VolumeParams; import org.panbox.core.keymgmt.VolumeParams.VolumeParamsFactory; import org.panbox.desktop.common.identitymgmt.sqlightimpl.AddressbookManager; import org.panbox.desktop.common.identitymgmt.sqlightimpl.IdentityManager; import org.panbox.desktop.common.vfs.DropboxVirtualVolume; import org.panbox.desktop.common.vfs.FuseUserFS; import org.panbox.desktop.common.vfs.PanboxFSLinux; import org.panbox.desktop.common.vfs.backend.VFSShare; import org.panbox.desktop.common.vfs.backend.VirtualRootVolume; import org.panbox.test.AbstractTest; /** * implemented tests: * <p/> * - create files and folders - rename file - obfuscation */ public class TestLinuxVFS extends AbstractTest { final char[] PASSWORD = "test".toCharArray(); final String DEVICE_NAME = "Laptop"; private SecretKey symKey; private ShareKey sk; private PublicKey mKey_pub; private PrivateKey mKey_priv; private PublicKey devKey_pub; private PrivateKey devKey_priv; @Rule public TemporaryFolder tmpTestDir = new TemporaryFolder(); private String mount; private String mountVFS; private String metadataPath; final static String TEST_SHARE_NAME = "testShare"; private String mountVFS_prefix; private static List<String> virtualFiles = new ArrayList<String>(); private static PanboxFSLinux loop = null; private SimpleAddressbook mAddressbook; @Before public void setUp() throws IOException, InterruptedException, ObfuscationException { // Hack to set Settings and Confdir in a way that doesn't affect the // real runtime environment setupSettings(); mAddressbook = new SimpleAddressbook(); mount = tmpTestDir.newFolder(".panboxTest").getAbsolutePath(); metadataPath = tmpTestDir.newFolder(".metadata").getAbsolutePath(); mountVFS = tmpTestDir.newFolder(".virtualPanbox").getAbsolutePath(); mountVFS_prefix = mountVFS + File.separator + TEST_SHARE_NAME; // Create identity createIdentity(); // Create metadata createMetadata(); // Retrieve the metadata Volume v = new Volume(metadataPath); try { v.loadShareMetaData(mKey_pub); } catch (Exception e2) { fail(e2.getMessage()); } // Get the encrypted obfuscationkey byte[] encObfKey; EncryptedShareKey esk; // ShareKey sk = null; try { encObfKey = v.getEncryptedObfuscationKey(devKey_pub); assertNotNull(encObfKey); symKey = CryptCore.decryptSymmertricKey(encObfKey, devKey_priv); assertNotNull(symKey); esk = v.getLatestEncryptedShareKey(devKey_pub); sk = new ShareKey(CryptCore.decryptSymmertricKey(esk.encryptedKey, devKey_priv), esk.version); } catch (SymmetricKeyNotFoundException e1) { fail(e1.getMessage()); } catch (SymmetricKeyDecryptionException e1) { fail(e1.getMessage()); } Obfuscator obfuscator = null; try { obfuscator = AbstractObfuscatorFactory.getFactory( FileObfuscatorFactory.class).getInstance(mount, "myShareName"); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ObfuscationException e1) { e1.printStackTrace(); fail(e1.getMessage()); } // files in Dropboxfolder debug("create list with files in dropbox (obfuscated)"); String unbenannterOrdner = obfuscator.obfuscate("Unbenannter Ordner", symKey, true); String testTxt = obfuscator.obfuscate("test.txt", symKey, true); String ordner1 = obfuscator.obfuscate("Ordner1", symKey, true); String ordner2 = obfuscator.obfuscate("Ordner2", symKey, true); String smartcardCfg = obfuscator.obfuscate("smartcard.cfg", symKey, true); List<String> dFiles = new ArrayList<String>(); dFiles.add(File.separator + unbenannterOrdner + File.separator + testTxt); dFiles.add(File.separator + ordner1 + File.separator + ordner2 + File.separator + testTxt); dFiles.add(File.separator + testTxt); dFiles.add(File.separator + smartcardCfg); // create subdirs debug("create testdirs"); new File(mount + File.separator + unbenannterOrdner).mkdirs(); new File(mount + File.separator + ordner1 + File.separator + ordner2) .mkdirs(); // expected files debug("create list with files in VFS (deobfucated)"); virtualFiles.add(File.separator + "Unbenannter Ordner" + File.separator + "test.txt"); virtualFiles.add(File.separator + "Ordner1" + File.separator + "Ordner2" + File.separator + "test.txt"); virtualFiles.add(File.separator + "test.txt"); virtualFiles.add(File.separator + "smartcard.cfg"); assertEquals("dFiles.size() != virtualFiles.size()", virtualFiles.size(), dFiles.size()); // create files for testing debug("create Files in " + mount); for (int i = 0; i < dFiles.size(); i++) { if ((dFiles.get(i).equals(obfuscator.obfuscatePath( virtualFiles.get(i), symKey, true))) && (virtualFiles.get(i).equals(obfuscator.deObfuscatePath( dFiles.get(i), symKey)))) { try { File f = new File(mount + dFiles.get(i)); AESGCMRandomAccessFile.create(sk.version, sk.key, f) .close(); } catch (Exception e) { fail("Can not create File: " + mount + dFiles.get(i) + "\n" + e.getMessage() + e.getStackTrace()); } } else { fail("deObfuscatePath(...) or obfuscatePath(...) does not work like expected (" + obfuscator.deObfuscatePath(dFiles.get(i), symKey) + " != " + virtualFiles.get(i) + ")"); } } } private ArrayList<String> getDir(String root, String path, ArrayList<String> list) { File dir = new File(root + path); File[] fileList = dir.listFiles(); for (File f : fileList) { if (f.isDirectory()) getDir(root, path + File.separator + f.getName(), list); else list.add(path + File.separator + f.getName()); } return list; } // collect filenames in List public ArrayList<String> getDir(String root) { return getDir(root, "", new ArrayList<String>()); } private void listCompare(List<String> listA, List<String> listB, String msg) { for (String a : listA) { boolean contains = false; for (String b : listB) { if (a.equals(b)) { contains = true; break; } } assertTrue("File " + a + " is not in " + msg, contains); } } @Test public void createTestFiles() throws InterruptedException { // create Dirs debug("create dirs " + mount + " and " + mountVFS); File mountPath = new File(mount); assertFalse("can't create dir: " + mount, !mountPath.exists() && !mountPath.mkdir()); File mountVirt = new File(mountVFS); assertFalse("can't create dir: " + mountVFS, !mountVirt.exists() && !mountVirt.mkdir()); // mount debug("mount"); DropboxVirtualVolume vfs; try { vfs = new DropboxVirtualVolume(mount); Volume v = new Volume(metadataPath); v.loadShareMetaData(mKey_pub); VirtualRootVolume.getInstance().registerShare( new VFSShare(TEST_SHARE_NAME, mount, vfs, v, new KeyPair( devKey_pub, devKey_priv))); } catch (Exception e1) { e1.printStackTrace(); fail(e1.getMessage()); } FuseUserFS fuse = new FuseUserFS(); loop = new PanboxFSLinux(fuse); // non-blocking vfs mount loop.mount(new File(mountVFS), false, null); while (!(new File(mountVFS_prefix)).exists()) { System.err.println("waiting for mountpoint .."); Thread.sleep(10); } ArrayList<String> al = getDir(mountVFS_prefix); // test, if Files in VFS are the same like expected debug("compare files in VFS and expected files"); listCompare(al, virtualFiles, "VFS"); listCompare(virtualFiles, al, "Dropbox"); // write plaintext, read plaintext debug("compare filecontent in VFS"); assertTrue("Writecontent failed", writeContent(virtualFiles)); assertTrue("Contentcheck failed", checkContent(virtualFiles)); debug("move file: " + virtualFiles.get(1) + " --> " + virtualFiles.get(1) + ".bak"); File source = new File(mountVFS_prefix + File.separator + "testfile.txt"); try { source.createNewFile(); debug("created: " + source.getName()); } catch (IOException e) { e.printStackTrace(); fail("Create file failed"); } File target = new File(mountVFS_prefix + File.separator + "testfile.bak"); source.renameTo(target); assertTrue("file move failed" + target.getAbsolutePath(), target.exists()); debug("file moved successful"); } private boolean writeContent(List<String> virtualFiles) { boolean res = true; for (String s : virtualFiles) { try { RandomAccessFile file = new RandomAccessFile(mountVFS_prefix + s, "rw"); file.write((s + "\tTestTestTest").getBytes()); file.close(); } catch (FileNotFoundException e) { res = false; e.printStackTrace(); } catch (IOException e) { res = false; e.printStackTrace(); } } return res; } private boolean checkContent(List<String> virtualFiles) { boolean res = true; for (String s : virtualFiles) { try { FileReader reader = new FileReader(mountVFS_prefix + s); BufferedReader br = new BufferedReader(reader); String content; while ((content = br.readLine()) != null) { assertTrue("ContentError: " + s, content.equals(s + "\tTestTestTest")); } reader.close(); } catch (Exception e) { res = false; } } return res; } public static boolean deleteDir(File path) { boolean res = true; for (File file : path.listFiles()) { if (file.isDirectory()) res &= deleteDir(file); res &= file.delete(); } return path.delete() & res; } @After public void tearDown() throws Exception { loop.unmount(); // deletion of VFS + backend Folders handled by junit } public void createIdentity() { // Delete old files IdentityManager idm = IdentityManager.getInstance(); AddressbookManager aBookMgr = new AddressbookManager(); idm.init(aBookMgr); Identity id = new Identity(mAddressbook); KeyPair ownerKeySign = CryptCore.generateKeypair(); assertNotNull(ownerKeySign); KeyPair ownerKeyEnc = CryptCore.generateKeypair(); assertNotNull(ownerKeyEnc); id.setOwnerKeySign(ownerKeySign, PASSWORD); id.setOwnerKeyEnc(ownerKeyEnc, PASSWORD); KeyPair deviceKeyLaptop = CryptCore.generateKeypair(); assertNotNull(deviceKeyLaptop); id.addDeviceKey(deviceKeyLaptop, DEVICE_NAME); id.setEmail("max@mustermann.org"); id.setFirstName("Max"); id.setName("Mustermann"); // store identity idm.storeMyIdentity(id); } public void createMetadata() { IdentityManager idm = IdentityManager.getInstance(); AddressbookManager aBookMgr = new AddressbookManager(); idm.init(aBookMgr); Identity loadedID = (Identity) idm.loadMyIdentity(mAddressbook); try { mKey_priv = loadedID.getPrivateKeySign(PASSWORD); } catch (UnrecoverableKeyException e2) { e2.printStackTrace(); } mKey_pub = loadedID.getPublicKeySign(); try { devKey_priv = loadedID.getPrivateKeyEnc(PASSWORD); } catch (UnrecoverableKeyException e1) { fail(); } devKey_pub = loadedID.getPublicKeyEnc(); VolumeParamsFactory pFac = VolumeParamsFactory.getFactory(); Volume volume = new Volume(metadataPath); try { VolumeParams p = pFac .createVolumeParams() .setOwnerAlias("Me") .setPublicSignatureKey(mKey_pub) .setPrivateSignatureKey(mKey_priv) .setPublicEncryptionKey(loadedID.getPublicKeyEnc()) .setPrivateEncryptionKey( loadedID.getPrivateKeyEnc(PASSWORD)) .setDeviceAlias(DEVICE_NAME).setPublicDeviceKey(devKey_pub); volume.createShareMetaData(p); } catch (IllegalArgumentException e) { fail(); } catch (ShareMetaDataException e) { fail(); } catch (UnrecoverableKeyException e) { fail(); } } }