/* * * 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.ByteArrayOutputStream; import java.io.IOException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.sql.SQLException; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.TreeMap; 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.EncodingHelper; import org.panbox.core.crypto.EncodingType; import org.panbox.core.crypto.KeyConstants; import org.panbox.core.crypto.Signable; import org.panbox.core.exception.DeviceListException; import org.panbox.core.exception.InitializaionException; import org.panbox.core.exception.PersistanceException; import org.panbox.core.exception.SerializationException; import org.panbox.core.exception.ShareMetaDataException; import org.panbox.core.exception.SymmetricKeyDecryptionException; import org.panbox.core.exception.SymmetricKeyEncryptionException; public final class ShareMetaData implements Signable { public static final Logger logger = Logger.getLogger(ShareMetaData.class); protected UUID id = null; protected SharePartList shareParticipants; protected TreeMap<PublicKey, DeviceList> deviceLists; protected ShareKeyDB shareKeys; protected String shareKeyAlgorithm = KeyConstants.SYMMETRIC_ALGORITHM; protected String publicKeyAlgorithm = KeyConstants.ASYMMETRIC_ALGORITHM; /** * Public Signature Key of share owner, used to verify the share participant * list */ protected final PublicKey ownerPubSigKey; protected ObfuscationKeyDB obfuscationKeys; protected String obfuscationKeyAlgorithm = KeyConstants.SYMMETRIC_ALGORITHM; byte[] signature; private final DBHelper db; /** * * @param url * - path incl filename for sqlite db (null = inmemory db) * @throws InitializaionException * @throws SequenceNumberException * @throws SignatureException */ ShareMetaData(DBHelper dbHelper, PublicKey owner) { this.ownerPubSigKey = owner; this.db = dbHelper; // this.deviceLists = new TreeMap<PublicKey, DeviceList>( Utils.PK_COMPARATOR); this.shareKeys = new ShareKeyDB(); this.obfuscationKeys = new ObfuscationKeyDB(); this.id = UUID.randomUUID(); // } SharePartList initSharePartList() { shareParticipants = new SharePartList(); return this.shareParticipants; } DeviceList createDeviceList(PublicKey key, Map<String, PublicKey> dkList) { if (dkList != null && !dkList.isEmpty() && key != null) { DeviceList deviceList = new DeviceList(key, dkList); this.deviceLists.put(key, deviceList); return deviceList; } else { throw new IllegalArgumentException( "key or dkList was either empty or null!"); } } Map<PublicKey, DeviceList> getDeviceLists() { return this.deviceLists; } ShareKeyDB getShareKeys() { return this.shareKeys; } /** * The sole use of this method is to retrieve the latest ShareKey to use * when encrypting files. * * @param pKey * PublicKey for the current user device * @return either an EncryptedShareKey Object or <code>null</code> if the * ShareKey Database contained no ShareKeys, which means, that the * corresponding volume has just been initialized or there is no * encrypted ShareKey for the given PublicKey available */ EncryptedShareKey getLatestEncryptedShareKey(PublicKey pKey) { ShareKeyDBEntry lastEntry = this.shareKeys.getLastEntry(); if (lastEntry != null) { byte[] encryptedKey = lastEntry.getEncryptedKey(pKey); if (encryptedKey != null) { return new EncryptedShareKey(encryptedKey, lastEntry.getVersion()); } } return null; } /** * This method is to be used for retrieving a user's device's ShareKey for * decrypting a file. * * @param version * The ShareKey version as retrieved from the encrypted file's * metadata * @param pKey * The PublicKey of the user's currently used device * @return either an EncryptedShareKey Object or <code>null</code> if the * ShareKey Database didn't hold ShareKeys of the given version or * there is no encrypted ShareKey for the given PublicKey available */ EncryptedShareKey getEncryptedShareKey(int version, PublicKey pKey) { ShareKeyDBEntry entry = this.shareKeys.getEntry(version); if (entry != null) { byte[] encryptedKey = entry.getEncryptedKey(pKey); if (encryptedKey != null) { return new EncryptedShareKey(encryptedKey, entry.getVersion()); } } return null; } /** * @param key * DeviceKey for which the encrypted * @return */ byte[] getEncryptedObfuscationKey(PublicKey key) { return obfuscationKeys.get(key); } byte[] removeObfuscationKey(PublicKey deviceKey) { return obfuscationKeys.remove(deviceKey); } void addObfuscationKey(PublicKey oldDevicePubKey, PrivateKey oldDevicePrivKey, PublicKey devicePubKey) throws SymmetricKeyEncryptionException, SymmetricKeyDecryptionException { if (oldDevicePubKey == null) { throw new IllegalArgumentException("Keys cannot be null!"); } if (this.obfuscationKeys.isEmpty()) { logger.debug("This is a new share: Initializing ObfuscationKey"); obfuscationKeys.add(oldDevicePubKey, CryptCore.encryptSymmetricKey( CryptCore.generateSymmetricKey().getEncoded(), oldDevicePubKey)); } else { if (oldDevicePrivKey == null || devicePubKey == null) { throw new IllegalArgumentException("Keys cannot be null!"); } SecretKey obKey = CryptCore.decryptSymmertricKey( getEncryptedObfuscationKey(oldDevicePubKey), oldDevicePrivKey); obfuscationKeys.add(devicePubKey, CryptCore.encryptSymmetricKey( obKey.getEncoded(), devicePubKey)); } } void addObfuscationKeys(PublicKey oldDevicePubKey, PrivateKey oldDevicePrivKey, Collection<PublicKey> deviceKeys) throws SymmetricKeyEncryptionException, SymmetricKeyDecryptionException { if (oldDevicePubKey == null) { throw new IllegalArgumentException("Keys cannot be null!"); } SecretKey obKey = null; if (this.obfuscationKeys.isEmpty()) { logger.debug("This is a new share: Initializing ObfuscationKey"); obKey = CryptCore.generateSymmetricKey(); obfuscationKeys.add(oldDevicePubKey, CryptCore.encryptSymmetricKey( obKey.getEncoded(), oldDevicePubKey)); } else { if (oldDevicePrivKey == null || deviceKeys == null) { throw new IllegalArgumentException("Keys cannot be null!"); } obKey = CryptCore.decryptSymmertricKey( getEncryptedObfuscationKey(oldDevicePubKey), oldDevicePrivKey); } byte[] encoded = obKey.getEncoded(); for (PublicKey deviceKey : deviceKeys) { obfuscationKeys.add(deviceKey, CryptCore.encryptSymmetricKey(encoded, deviceKey)); } } byte[] getSignature() { return this.signature; } SharePartList getSharePartList() { return this.shareParticipants; } @Override public byte[] serialize() throws SerializationException { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); os.write(id.toString().getBytes()); os.write(obfuscationKeyAlgorithm.getBytes()); os.write(shareKeyAlgorithm.getBytes()); os.write(publicKeyAlgorithm.getBytes()); os.write(shareParticipants.serialize()); for (PublicKey key : deviceLists.keySet()) { os.write(key.getEncoded()); os.write(deviceLists.get(key).serialize()); } os.write(shareKeys.serialize()); os.write(obfuscationKeys.serialize()); return os.toByteArray(); } catch (IOException e) { throw new SerializationException( "Could not serialize Share Metadata", e); } } void persist() throws ShareMetaDataException { try { this.db.storeSPL(this); } catch (PersistanceException e) { throw new ShareMetaDataException("Could not persist ShareMetaData", e); } } void persist(DeviceList devices) throws ShareMetaDataException { try { this.db.store(devices, this.obfuscationKeys, this.shareKeys); } catch (PersistanceException e) { throw new ShareMetaDataException("Could not persist ShareMetaData", e); } } /** * @throws DeviceListException * @throws SignatureException * @throws InitializaionException * If old Sharemetadata object exists: reload its content from * db * * @return true is successful * @throws SequenceNumberException * @throws */ void load() throws InitializaionException, SignatureException, DeviceListException { try { this.db.load(this); } catch (SQLException e) { throw new InitializaionException("Could not load share metadata", e); } } UUID getUUID() { return this.id; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ShareMetaData (ID: " + id.toString() + ")\n"); sb.append(shareParticipants); sb.append("DeviceLists:\n"); for (PublicKey key : deviceLists.keySet()) { sb.append("\tPK: " + EncodingHelper.encodeByte(key.getEncoded(), EncodingType.BASE64) + "\n"); sb.append(deviceLists.get(key)); } sb.append(shareKeys); sb.append("--End Signature--\n"); sb.append("--End ShareMetaData--\n"); return sb.toString(); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (obj.getClass() != getClass()) return false; ShareMetaData smd = (ShareMetaData) obj; return ((smd.deviceLists == deviceLists) || (smd.deviceLists != null && deviceLists != null && Utils.valueEquals(smd.deviceLists, deviceLists))) && ((smd.id == id) || (smd.id != null && smd.id.equals(id))) && ((smd.obfuscationKeyAlgorithm == obfuscationKeyAlgorithm) || (smd.obfuscationKeyAlgorithm != null && smd.obfuscationKeyAlgorithm .equals(obfuscationKeyAlgorithm))) && ((smd.obfuscationKeys == obfuscationKeys) || (smd.obfuscationKeys != null && smd.obfuscationKeys .equals(obfuscationKeys))) && (ownerPubSigKey != null ? ownerPubSigKey .equals(smd.ownerPubSigKey) : smd.ownerPubSigKey == null) && ((publicKeyAlgorithm == smd.publicKeyAlgorithm) || (smd.publicKeyAlgorithm != null && smd.publicKeyAlgorithm .equals(publicKeyAlgorithm))) && ((smd.shareKeys == shareKeys) || (smd.shareKeys != null && smd.shareKeys .equals(shareKeys))) && ((smd.shareKeyAlgorithm == shareKeyAlgorithm) || (smd.shareKeyAlgorithm != null && smd.shareKeyAlgorithm .equals(shareKeyAlgorithm))) && ((smd.shareParticipants == shareParticipants) || (smd.shareParticipants != null && smd.shareParticipants .equals(shareParticipants))) && ((signature == smd.signature) || (smd.signature != null && signature != null && Arrays.equals(smd.signature, signature))); } @Override public int hashCode() { int hc = 11; int mul = 37; hc = hc * mul; hc += (deviceLists == null ? 0 : deviceLists.hashCode()); hc += (id == null ? 0 : id.hashCode()); hc += (obfuscationKeyAlgorithm == null ? 0 : obfuscationKeyAlgorithm .hashCode()); hc += (obfuscationKeys == null ? 0 : obfuscationKeys.hashCode()); hc += (ownerPubSigKey == null ? 0 : ownerPubSigKey.hashCode()); hc += (publicKeyAlgorithm == null ? 0 : publicKeyAlgorithm.hashCode()); hc += (shareKeys == null ? 0 : shareKeys.hashCode()); hc += (shareKeyAlgorithm == null ? 0 : shareKeyAlgorithm.hashCode()); hc += (shareParticipants == null ? 0 : shareParticipants.hashCode()); if (signature != null) { for (int i = 0; i < signature.length; i++) { hc += signature[i]; } } return hc; } }