/*
*
* 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.core.keymgmt;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.util.Arrays;
import java.util.TreeMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.panbox.core.Utils;
import org.panbox.core.crypto.CryptCore;
import org.panbox.core.exception.ShareMetaDataException;
import org.panbox.core.identitymgmt.Identity;
import org.panbox.core.identitymgmt.SimpleAddressbook;
import org.panbox.core.keymgmt.VolumeParams.VolumeParamsFactory;
public class TestUserInvite {
static PrivateKey A_Key_sig_priv;
static PublicKey A_Key_sig_pub;
static PrivateKey A_Key_enc_priv;
static PublicKey A_Key_enc_pub;
static PublicKey A_devKey_enc_pub;
static PrivateKey A_devKey_enc_priv;
static PrivateKey B_Key_sig_priv;
static PublicKey B_Key_sig_pub;
static PrivateKey B_Key_enc_priv;
static PublicKey B_Key_enc_pub;
static PublicKey B_devKey_enc_pub;
static PrivateKey B_devKey_enc_priv;
private String dbName;
private static String aliasOwner = "Owner";
private static String aliasGuest = "Guest";
private static String deviceAliasOwner = "OwnerDev";
private static String deviceAliasGuest = "GuestDev";
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@BeforeClass
public static void setUpClass() {
try {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA",
new BouncyCastleProvider());
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
gen.initialize(1024, random);
KeyPair pair = gen.generateKeyPair();
A_Key_sig_priv = pair.getPrivate();
A_Key_sig_pub = pair.getPublic();
gen.generateKeyPair();
A_Key_enc_priv = pair.getPrivate();
A_Key_enc_pub = pair.getPublic();
pair = gen.generateKeyPair();
A_devKey_enc_priv = pair.getPrivate();
A_devKey_enc_pub = pair.getPublic();
pair = gen.generateKeyPair();
B_Key_sig_priv = pair.getPrivate();
B_Key_sig_pub = pair.getPublic();
gen.generateKeyPair();
B_Key_enc_priv = pair.getPrivate();
B_Key_enc_pub = pair.getPublic();
pair = gen.generateKeyPair();
B_devKey_enc_priv = pair.getPrivate();
B_devKey_enc_pub = pair.getPublic();
} catch (NoSuchAlgorithmException e) {
fail(e.getMessage());
}
}
@Before
public void setUp() throws NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, IOException {
File newFolder = folder.newFolder(".metadata");
System.out.println("Create temporary folder: " + newFolder.mkdirs());
dbName = newFolder.getAbsolutePath();
File db = new File(dbName + File.separator + Volume.DB_FILE);
if (db.exists())
db.delete();
}
@Test
public void test() {
VolumeParamsFactory pf = VolumeParamsFactory.getFactory();
// Create Share
IVolume volume = new Volume(dbName);
assertNotNull(volume);
ShareMetaData smd = null;
VolumeParams p = pf.createVolumeParams().setOwnerAlias(aliasOwner)
.setPublicSignatureKey(A_Key_sig_pub)
.setPrivateSignatureKey(A_Key_sig_priv)
.setPublicEncryptionKey(A_Key_enc_pub)
.setPrivateEncryptionKey(A_Key_enc_priv)
.setDeviceAlias(deviceAliasOwner)
.setPublicDeviceKey(A_devKey_enc_pub);
try {
smd = volume.createShareMetaData(p);
assertNotNull(smd);
} catch (Exception e) {
fail(e.getMessage());
}
assertEquals(1, smd.getSharePartList().size());
assertEquals(A_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasOwner));
assertNull(smd.getSharePartList().getPublicKey(aliasGuest));
assertEquals(1, smd.shareKeys.size());
assertEquals(2, smd.shareKeys.getLastEntry().size());
// Invite user
p.setOtherSignatureKey(B_Key_sig_pub)
.setOtherEncryptionKey(B_Key_enc_pub).setUserAlias(aliasGuest);
try {
volume.inviteUser(p);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
IVolume volume2 = new Volume(dbName);
try {
smd = volume2.loadShareMetaData(A_Key_sig_pub);
assertNotNull(smd);
} catch (Exception e) {
e.printStackTrace(System.err);
fail(e.getMessage());
}
assertEquals(2, smd.getSharePartList().size());
assertEquals(A_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasOwner));
assertEquals(B_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasGuest));
assertEquals(1, smd.shareKeys.size());
assertEquals(3, smd.shareKeys.getLastEntry().size());
try {
ObfuscationKeyDB okdb = smd.obfuscationKeys;
Field f = ObfuscationKeyDB.class
.getDeclaredField("obfuscationKeys");
f.setAccessible(true);
@SuppressWarnings("unchecked")
TreeMap<PublicKey, byte[]> obkeys = (TreeMap<PublicKey, byte[]>) f
.get(okdb);
int size = obkeys.size();
assertEquals(3, size);
assertTrue(obkeys.containsKey(A_Key_enc_pub));
assertTrue(obkeys.containsKey(A_devKey_enc_pub));
assertTrue(obkeys.containsKey(B_Key_enc_pub));
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
// accept invitation
volume2 = new Volume(dbName);
VolumeParams p2 = pf.createVolumeParams()
.setOwnerSignatureKey(A_Key_sig_pub).setUserAlias(aliasGuest)
.setPublicDeviceKey(B_devKey_enc_pub)
.setPublicEncryptionKey(B_Key_enc_pub)
.setPublicSignatureKey(B_Key_sig_pub)
.setPrivateEncryptionKey(B_Key_enc_priv)
.setPrivateSignatureKey(B_Key_sig_priv)
.setDeviceAlias(deviceAliasGuest);
try {
smd = volume2.acceptInvitation(p2);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
assertEquals(2, smd.getSharePartList().size());
assertEquals(A_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasOwner));
assertEquals(B_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasGuest));
assertEquals(1, smd.shareKeys.size());
assertEquals(4, smd.shareKeys.getLastEntry().size());
try {
ObfuscationKeyDB okdb = smd.obfuscationKeys;
Field f = ObfuscationKeyDB.class
.getDeclaredField("obfuscationKeys");
f.setAccessible(true);
@SuppressWarnings("unchecked")
TreeMap<PublicKey, byte[]> obkeys = (TreeMap<PublicKey, byte[]>) f
.get(okdb);
int size = obkeys.size();
assertEquals(4, size);
assertTrue(obkeys.containsKey(A_Key_enc_pub));
assertTrue(obkeys.containsKey(A_devKey_enc_pub));
assertTrue(obkeys.containsKey(B_Key_enc_pub));
assertTrue(obkeys.containsKey(B_devKey_enc_pub));
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
// reload share to verify integrity
IVolume volume3 = new Volume(dbName);
try {
smd = volume3.loadShareMetaData(A_Key_sig_pub);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
assertEquals(2, smd.getSharePartList().size());
assertEquals(A_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasOwner));
assertEquals(B_Key_sig_pub,
smd.getSharePartList().getPublicKey(aliasGuest));
assertEquals(1, smd.shareKeys.size());
assertEquals(4, smd.shareKeys.getLastEntry().size());
try {
ObfuscationKeyDB okdb = smd.obfuscationKeys;
Field f = ObfuscationKeyDB.class
.getDeclaredField("obfuscationKeys");
f.setAccessible(true);
@SuppressWarnings("unchecked")
TreeMap<PublicKey, byte[]> obkeys = (TreeMap<PublicKey, byte[]>) f
.get(okdb);
int size = obkeys.size();
assertEquals(4, size);
assertTrue(obkeys.containsKey(A_Key_enc_pub));
assertTrue(obkeys.containsKey(A_devKey_enc_pub));
assertTrue(obkeys.containsKey(B_Key_enc_pub));
assertTrue(obkeys.containsKey(B_devKey_enc_pub));
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void testKey() {
SimpleAddressbook ab = new SimpleAddressbook();
Identity owner = new Identity(ab, "ali@baba.de", "Uli", "Mayer");
KeyPair ownerKeySign = CryptCore.generateKeypair();
KeyPair ownerKeyEnc = CryptCore.generateKeypair();
KeyPair ownerDeviceEnc = CryptCore.generateKeypair();
String ownerDev = "pc-owner";
owner.setOwnerKeyEnc(ownerKeyEnc, "123456".toCharArray());
owner.setOwnerKeySign(ownerKeySign, "123456".toCharArray());
owner.addDeviceKey(ownerDeviceEnc, ownerDev);
PrivateKey origKey = ownerKeySign.getPrivate();
PrivateKey identKey = null;
try {
identKey = owner.getPrivateKeySign("123456".toCharArray());
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
byte[] original = origKey.getEncoded();
assertNotNull(identKey);
byte[] fromIdentity = identKey.getEncoded();
assertTrue(Arrays.equals(original, fromIdentity));
assertEquals(origKey.getAlgorithm(), identKey.getAlgorithm());
assertEquals(origKey.getFormat(), identKey.getFormat());
assertTrue(Utils.keysMatch(ownerKeySign.getPublic(), identKey));
assertTrue(Utils.keysMatch(owner.getPublicKeySign(),
ownerKeySign.getPrivate()));
}
@Test
public void testIntegration() throws InterruptedException {
System.out.println("Creating share");
SimpleAddressbook ab = new SimpleAddressbook();
Identity owner = new Identity(ab, "ali@baba.de", "Uli", "Mayer");
KeyPair ownerKeySign = CryptCore.generateKeypair();
KeyPair ownerKeyEnc = CryptCore.generateKeypair();
KeyPair ownerDeviceEnc = CryptCore.generateKeypair();
String ownerDev = "pc-owner";
owner.setOwnerKeyEnc(ownerKeyEnc, "123456".toCharArray());
owner.setOwnerKeySign(ownerKeySign, "123456".toCharArray());
owner.addDeviceKey(ownerDeviceEnc, ownerDev);
SimpleAddressbook ab2 = new SimpleAddressbook();
Identity guest = new Identity(ab2, "peter@mueller.de", "Peter",
"Müller");
KeyPair guestKeySign = CryptCore.generateKeypair();
KeyPair guestKeyEnc = CryptCore.generateKeypair();
KeyPair guestDeviceEnc = CryptCore.generateKeypair();
String guestDev = "PC-GUEST";
// String guestDev = "pc-guest";
guest.setOwnerKeyEnc(guestKeyEnc, "7890".toCharArray());
guest.setOwnerKeySign(guestKeySign, "7890".toCharArray());
guest.addDeviceKey(guestDeviceEnc, guestDev);
VolumeParamsFactory pf = VolumeParamsFactory.getFactory();
String path = dbName;
IVolume v1 = new Volume(path);
VolumeParams p = null;
try {
p = pf.createVolumeParams().setOwnerAlias(owner.getEmail())
.setKeys(owner, "123456".toCharArray())
.setDeviceAlias(ownerDev)
.setPublicDeviceKey(owner.getPublicKeyForDevice(ownerDev));
} catch (UnrecoverableKeyException e2) {
e2.printStackTrace();
fail(e2.getMessage());
}
try {
@SuppressWarnings("unused")
// just for testing functionality
ShareMetaData smd = v1.createShareMetaData(p);
} catch (IllegalArgumentException | ShareMetaDataException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("loading share");
try {
new Volume(path).loadShareMetaData(owner.getPublicKeySign());
} catch (IllegalArgumentException | ShareMetaDataException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
}
System.out.println("Inviting user to share");
p.setOtherEncryptionKey(guest.getPublicKeyEnc())
.setOtherSignatureKey(guest.getPublicKeySign())
.setUserAlias(guest.getEmail());
try {
v1.inviteUser(p);
} catch (ShareMetaDataException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("accepting invitation to share");
IVolume v2 = new Volume(path);
try {
VolumeParams p2 = pf.createVolumeParams()
.setKeys(guest, "7890".toCharArray())
.setUserAlias(guest.getEmail()).setDeviceAlias(guestDev)
.setPublicDeviceKey(guest.getPublicKeyForDevice(guestDev))
.setOwnerSignatureKey(owner.getPublicKeySign());
@SuppressWarnings("unused")
// just for testing functionality
ShareMetaData smd2 = v2.acceptInvitation(p2);
} catch (UnrecoverableKeyException | ShareMetaDataException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("Loading share");
IVolume v3 = new Volume(path);
try {
PublicKey publicKeySign = owner.getPublicKeySign();
v3.loadShareMetaData(publicKeySign);
} catch (ShareMetaDataException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}