/*
*
* 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 java.io.File;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.UUID;
import javax.crypto.SecretKey;
import org.apache.log4j.Logger;
import org.panbox.core.Utils;
import org.panbox.core.crypto.CryptCore;
import org.panbox.core.crypto.SignatureHelper;
import org.panbox.core.exception.DeviceListException;
import org.panbox.core.exception.InitializaionException;
import org.panbox.core.exception.SerializationException;
import org.panbox.core.exception.ShareMetaDataException;
import org.panbox.core.exception.SymmetricKeyDecryptionException;
import org.panbox.core.exception.SymmetricKeyEncryptionException;
import org.panbox.core.exception.SymmetricKeyNotFoundException;
/**
* @author Sinisa Dukanovic
*
*/
public class Volume implements IVolume {
private final static Logger logger = Logger.getLogger(Volume.class);
public final static String DB_FILE = "pbmeta.db";
public final static String SPL_FILE = "pb_spl.db";
public final static String KEYS_FILE = "pb_keys.db";
private final DBHelper db;
private ShareMetaData shareMetaData;
/**
* Default Constructor for Panpox Volumes
*
* @param path
*/
public Volume(String path) throws IllegalArgumentException {
this(new JDBCHelperNonRevokeable(getPath(path)));
}
public Volume(DBHelper dbHelper) throws IllegalArgumentException {
this.db = dbHelper;
if (dbHelper == null) {
throw new IllegalAccessError("Database Helper can't be null");
}
}
@Override
public ShareMetaData loadShareMetaData(PublicKey owner)
throws ShareMetaDataException {
// logger.info("Loading ShareMetaData from db file: " + path);
logger.info("Loading ShareMetaData from db");
if (this.shareMetaData == null) {
this.shareMetaData = new ShareMetaData(this.db, owner);
try {
this.shareMetaData.load();
} catch (InitializaionException e) {
throw new ShareMetaDataException(
"Could not initialize ShareMetaData", e);
} catch (SignatureException e) {
throw new ShareMetaDataException("Could not verify signature",
e);
} catch (DeviceListException e) {
throw new ShareMetaDataException(
"Could not verify signature of device list", e);
}
}
return this.shareMetaData;
}
private static String getPath(final String path) {
String url = "jdbc:sqlite:";
if (path == null) {
logger.info("Choosing inmemory db");
} else {
logger.info("Choosing " + path + " as metadata database");
url = url + path
+ (path.endsWith(File.separator) ? "" : File.separator);
}
return url;
}
@Override
public UUID getUUID() {
return this.shareMetaData.getUUID();
}
@Override
public ShareMetaData createShareMetaData(VolumeParams p)
throws IllegalArgumentException, ShareMetaDataException {
return this.createShareMetaData(p.ownerAlias, p.pubSigKey,
p.privSigKey, p.pubEncKey, p.privEncKey, p.deviceAlias,
p.deviceKey);
}
@Override
public ShareMetaData createShareMetaData(String ownerAlias,
PublicKey ownerPubSigKey, PrivateKey ownerPrivSigKey,
PublicKey ownerPubEncKey, PrivateKey ownerPrivEncKey,
String deviceAlias, PublicKey devicePubKey)
throws IllegalArgumentException, ShareMetaDataException {
if (IVolume.MASTER_KEY.equals(deviceAlias)) {
throw new IllegalArgumentException(
"Illegal alias for device. Choose another alias");
}
if (db.exists()) {
throw new IllegalArgumentException(
"Can't initialize new share metadata, because database already exists.");
}
// verify matching public/private keys
if (!Utils.keysMatch(ownerPubSigKey, ownerPrivSigKey)) {
throw new IllegalArgumentException(
"Owners master private and public key do not match!");
}
shareMetaData = new ShareMetaData(db, ownerPubSigKey);
try {
this.shareMetaData.load();
} catch (InitializaionException e) {
throw new ShareMetaDataException(
"Could not initialize ShareMetaData", e);
} catch (SignatureException e) {
throw new ShareMetaDataException("Could not verify signature", e);
} catch (DeviceListException e) {
throw new ShareMetaDataException("Could not verify signature", e);
}
// Init Participants list
SharePartList spl = shareMetaData.initSharePartList();
spl.add(ownerPubSigKey, ownerAlias);
// Init DeviceList
HashMap<String, PublicKey> dkList = new HashMap<String, PublicKey>();
dkList.put(IVolume.MASTER_KEY, ownerPubEncKey);
dkList.put(deviceAlias, devicePubKey);
DeviceList devices = shareMetaData.createDeviceList(ownerPubSigKey,
dkList);
// Create ObfuscationKey
try {
shareMetaData.addObfuscationKey(ownerPubEncKey, null, null);
shareMetaData.addObfuscationKey(ownerPubEncKey, ownerPrivEncKey,
devicePubKey);
} catch (SymmetricKeyEncryptionException
| SymmetricKeyDecryptionException e) {
throw new ShareMetaDataException(
"Could not initialize obfuscation key", e);
}
// Create initial ShareKey
ShareKeyDB shareKeys = shareMetaData.getShareKeys();
SecretKey shareKey = CryptCore.generateSymmetricKey();
try {
shareKeys.add(shareKey, dkList.values());
} catch (SymmetricKeyEncryptionException e1) {
throw new ShareMetaDataException(
"Could not encrypt sharekey for device", e1);
}
try {
spl.sign(ownerPrivSigKey);
} catch (SignatureException e2) {
throw new ShareMetaDataException("Could not sign SharePartList", e2);
}
try {
devices.sign(ownerPrivSigKey, shareKeys,
shareMetaData.obfuscationKeys);
} catch (SignatureException e2) {
throw new ShareMetaDataException("Could not sign DeviceList", e2);
}
shareMetaData.persist();
shareMetaData.persist(devices);
return shareMetaData;
}
@Override
public byte[] getEncryptedObfuscationKey(PublicKey devicePubKey)
throws SymmetricKeyNotFoundException {
byte[] result = this.shareMetaData
.getEncryptedObfuscationKey(devicePubKey);
if (result == null) {
throw new SymmetricKeyNotFoundException(
SymmetricKeyNotFoundException.OBFUSCATION, devicePubKey);
}
return result;
}
@Override
public EncryptedShareKey getEncryptedShareKey(int version,
PublicKey devicePubKey) throws SymmetricKeyNotFoundException {
EncryptedShareKey result = this.shareMetaData.getEncryptedShareKey(
version, devicePubKey);
if (result == null) {
throw new SymmetricKeyNotFoundException(
SymmetricKeyNotFoundException.FILE_ENCRYPTION, devicePubKey);
}
return result;
}
@Override
public EncryptedShareKey getLatestEncryptedShareKey(PublicKey devicePubKey)
throws SymmetricKeyNotFoundException {
EncryptedShareKey result = this.shareMetaData
.getLatestEncryptedShareKey(devicePubKey);
if (result == null) {
throw new SymmetricKeyNotFoundException(
SymmetricKeyNotFoundException.FILE_ENCRYPTION, devicePubKey);
}
return result;
}
@Override
public void addDevice(VolumeParams p) throws IllegalArgumentException,
ShareMetaDataException {
this.addDevice(p.userAlias, p.pubSigKey, p.privSigKey, p.deviceAlias,
p.deviceKey, p.pubEncKey, p.privEncKey);
}
@Override
public void addDevice(String alias, PublicKey masterPubSigKey,
PrivateKey masterPrivSigKey, String newDeviceAlias,
PublicKey newDevicePubKey, PublicKey masterPubEncKey,
PrivateKey masterPrivEncKey) throws ShareMetaDataException {
if (IVolume.MASTER_KEY.equals(newDeviceAlias)) {
throw new IllegalArgumentException(
"Illegal alias for device. Choose another alias");
}
// verify matching public/private keys
if (!Utils.keysMatch(masterPubSigKey, masterPrivSigKey)) {
throw new IllegalArgumentException(
"User's master private and public signature keys do not match!");
}
if (!Utils.keysMatch(masterPubEncKey, masterPrivEncKey)) {
throw new IllegalArgumentException(
"User's master private and public encryption keys do not match!");
}
// verify integrity of ShareParticipantList
SharePartList sharePartList = shareMetaData.getSharePartList();
try {
SignatureHelper.verify(sharePartList, sharePartList.getSignature(),
shareMetaData.ownerPubSigKey);
} catch (InvalidKeyException | NoSuchAlgorithmException
| SignatureException | SerializationException e) {
throw new ShareMetaDataException(
"Could not verify ShareParticipantsList signature", e);
}
// check if masterPubSigKey is in ShareParticipants
if (!sharePartList.getPublicKey(alias).equals(masterPubSigKey)) {
throw new ShareMetaDataException(
"Given user singature publickey is not "
+ "in sharepartiticapnts list");
}
// Get DeviceList for user
DeviceList deviceList = shareMetaData.getDeviceLists().get(
masterPubSigKey);
if (deviceList == null) {
throw new ShareMetaDataException("DeviceList for user " + alias
+ " was empty, which should never be the case.");
}
// add device
deviceList.addDevice(newDeviceAlias, newDevicePubKey);
// add encrypted Obfuscation key for new device
try {
shareMetaData.addObfuscationKey(masterPubEncKey, masterPrivEncKey,
newDevicePubKey);
} catch (SymmetricKeyEncryptionException
| SymmetricKeyDecryptionException e) {
throw new ShareMetaDataException(
"Could not add encrypted obfuscation key for new device", e);
}
// add encrypted sharekey for device
try {
this.shareMetaData.shareKeys.addDevice(masterPubEncKey,
masterPrivEncKey, newDevicePubKey);
} catch (Exception e) {
throw new ShareMetaDataException(
"Could not add encrypted share keys for new device", e);
}
// Sign everything
try {
deviceList.sign(masterPrivSigKey, shareMetaData.shareKeys,
shareMetaData.obfuscationKeys);
} catch (SignatureException e) {
throw new ShareMetaDataException("Could not sign devicelist", e);
}
this.shareMetaData.persist(deviceList);
}
@Override
public void removeDevice(VolumeParams p) throws IllegalArgumentException,
ShareMetaDataException {
this.removeDevice(p.userAlias, p.pubSigKey, p.privSigKey, p.deviceAlias);
}
@Override
public void removeDevice(String alias, PublicKey masterPubSigKey,
PrivateKey masterPrivSigKey, String deviceAlias)
throws ShareMetaDataException {
throw new ShareMetaDataException("Not yet implemented");
/*
* // Check name of device that should be removed if
* (IVolume.MASTER_KEY.equals(deviceAlias)) { throw new
* IllegalArgumentException(
* "Illegal alias for device. Choose another alias"); }
*
* // verify matching public/private keys if
* (!Utils.keysMatch(masterPubSigKey, masterPrivSigKey)) { throw new
* ShareMetaDataException(
* "Owners master private and public key do not match!"); }
*
* // check if masterPubSigKey is in ShareParticipants if
* (!shareMetaData.getSharePartList().getPublicKey(alias)
* .equals(masterPubSigKey)) { throw new ShareMetaDataException(
* "Given master singature publickey is not " +
* "in sharepartiticapnts list"); }
*
* // Get DeviceList for user DeviceList deviceList =
* this.shareMetaData.getDeviceLists().get( masterPubSigKey);
*
* if (deviceList == null || deviceList.getPublicKey(deviceAlias) ==
* null) { // Device wasn't in list... exit method here return; } //
* verify existing list and add device boolean verified = false; try {
* verified = SignatureHelper.verify(deviceList,
* deviceList.getSignature(), masterPubSigKey); } catch (Exception e) {
* throw new ShareMetaDataException(
* "Could not verify DeviceList signature", e); } if (!verified) { throw
* new ShareMetaDataException(
* "Could not verify DeviceList with the given PublicKey", new
* SignatureException( "DeviceList signature verification failed")); }
* PublicKey deviceKey = deviceList.removeDevice(deviceAlias);
*
* try { deviceList.sign(masterPrivSigKey); } catch (SignatureException
* e) { throw new ShareMetaDataException("Could not sign devicelist",
* e); }
*
* // remove encrypted Obfuscation key for new device byte[] obKey =
* shareMetaData.removeObfuscationKey(deviceKey); if (obKey != null) {
* try { this.shareMetaData.signObfuscationKeys(masterPrivSigKey,
* alias); } catch (SignatureException e) { throw new
* ShareMetaDataException( "Could not sign obfuscation keys", e); } }
*
* // remove encrypted sharekey for device
* this.shareMetaData.shareKeys.removeDevice(deviceKey);
*
* // add new sharekey version for the share try {
* this.shareMetaData.shareKeys.add(CryptCore.generateSymmetricKey()); }
* catch (SymmetricKeyEncryptionException e) { throw new
* ShareMetaDataException(
* "Could not add new sharekey version to sharekeydb.", e); }
*
* try { this.shareMetaData.shareKeys.sign(masterPrivSigKey, alias); }
* catch (SignatureException e) { throw new
* ShareMetaDataException("Could not sign sharekeys", e); }
*
* try { this.shareMetaData.sign(masterPrivSigKey, alias); } catch
* (SignatureException e) { throw new
* ShareMetaDataException("Could not sign share metadata", e); }
* this.shareMetaData.persist();
*/
}
@Override
public void inviteUser(VolumeParams p) throws ShareMetaDataException {
this.inviteUser(p.ownerAlias, p.privSigKey, p.pubEncKey, p.privEncKey,
p.userAlias, p.otherEncKey, p.otherSigKey);
}
@Override
public void inviteUser(String ownerAlias, PrivateKey ownerPrivSigKey,
PublicKey ownerPubEncKey, PrivateKey ownerPrivEncKey, String alias,
PublicKey userPubEncKey, PublicKey userPubSigKey)
throws ShareMetaDataException {
// Check if called by owner
if (!Utils.keysMatch(shareMetaData.ownerPubSigKey, ownerPrivSigKey)) {
throw new ShareMetaDataException(
"This method can only be called by the share owner!");
}
SharePartList spl = shareMetaData.getSharePartList();
// Check if user alias already exists
if (spl.getPublicKey(alias) == null) {
spl.add(userPubSigKey, alias);
}
// Create deviceList and sign for user
HashMap<String, PublicKey> dkList = new HashMap<String, PublicKey>();
dkList.put(IVolume.MASTER_KEY, userPubEncKey);
DeviceList devices = shareMetaData.createDeviceList(userPubSigKey,
dkList);
try {
shareMetaData.addObfuscationKey(ownerPubEncKey, ownerPrivEncKey,
userPubEncKey);
} catch (SymmetricKeyEncryptionException
| SymmetricKeyDecryptionException e) {
throw new ShareMetaDataException(
"Could not add encrypted obfuscation key for new user", e);
}
// add encrypted sharekey for device
try {
this.shareMetaData.shareKeys.addDevice(ownerPubEncKey,
ownerPrivEncKey, userPubEncKey);
} catch (Exception e) {
throw new ShareMetaDataException(
"Could not add encrypted share keys for new user", e);
}
// Sign everything
try {
spl.sign(ownerPrivSigKey);
} catch (SignatureException e) {
throw new ShareMetaDataException(
"Could not add new user to ShareParticipantList.", e);
}
try {
devices.sign(ownerPrivSigKey, shareMetaData.shareKeys,
shareMetaData.obfuscationKeys);
} catch (SignatureException e) {
throw new ShareMetaDataException("Could not sign devicelist", e);
}
this.shareMetaData.persist();
this.shareMetaData.persist(devices);
}
@Override
public ShareMetaData acceptInvitation(VolumeParams p)
throws ShareMetaDataException {
return this.acceptInvitation(p.ownerSigKey, p.userAlias, p.pubSigKey,
p.privSigKey, p.deviceAlias, p.deviceKey, p.pubEncKey,
p.privEncKey);
}
@Override
public ShareMetaData acceptInvitation(PublicKey ownerPubSigKey,
String alias, PublicKey masterPubSigKey,
PrivateKey masterPrivSigKey, String deviceAlias,
PublicKey newDevicePubKey, PublicKey masterPubEncKey,
PrivateKey masterPrivEncKey) throws ShareMetaDataException {
loadShareMetaData(ownerPubSigKey);
addDevice(alias, masterPubSigKey, masterPrivSigKey, deviceAlias,
newDevicePubKey, masterPubEncKey, masterPrivEncKey);
return this.shareMetaData;
}
@Override
public void removeUser(String ownerAlias, PublicKey ownerPubSigKey,
PrivateKey ownerPrivSigKey, String userAlias)
throws ShareMetaDataException {
// TODO Implement secure remove user feature
throw new ShareMetaDataException("Not yet implemented");
}
@Override
public HashMap<PublicKey, String> getShareParticipants() {
HashMap<PublicKey, String> result = new HashMap<PublicKey, String>(
this.shareMetaData.shareParticipants.size());
Iterator<String> it = this.shareMetaData.shareParticipants.getAliases();
while (it.hasNext()) {
String alias = (String) it.next();
PublicKey key = this.shareMetaData.shareParticipants
.getPublicKey(alias);
result.put(key, alias);
}
return result;
}
@Override
public HashMap<PublicKey, String> getDeviceMap(PublicKey pubSigKey) {
DeviceList list = this.shareMetaData.deviceLists.get(pubSigKey);
HashMap<PublicKey, String> result = new HashMap<>(list.size());
Iterator<String> it = list.getAliasIterator();
while (it.hasNext()) {
String alias = (String) it.next();
if (!IVolume.MASTER_KEY.equals(alias)) {
result.put(list.getPublicKey(alias), alias);
}
}
return result;
}
@Override
public PublicKey getOwnerKey() {
return this.shareMetaData.ownerPubSigKey;
}
@Override
public void reload() throws ShareMetaDataException {
PublicKey ownerKey = this.shareMetaData.ownerPubSigKey;
this.shareMetaData = null;
this.loadShareMetaData(ownerKey);
}
}