/*
*
* 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.PublicKey;
import java.security.SignatureException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.TreeMap;
import java.util.UUID;
import org.panbox.core.Utils;
import org.panbox.core.crypto.CryptCore;
import org.panbox.core.crypto.KeyConstants;
import org.panbox.core.crypto.Signable;
import org.panbox.core.crypto.SignatureHelper;
import org.panbox.core.exception.InitializaionException;
import org.panbox.core.exception.PersistanceException;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;
public class AndroidJDBCHelperNonRevokeable implements DBHelper {
private final String splPath;
private final String devListPath;
private final PublicKey masterPubKey;
public AndroidJDBCHelperNonRevokeable(String splPath, String devListPath,
PublicKey masterPubKey) {
Log.i(AndroidJDBCHelperNonRevokeable.class.getName(),
"Creating android database handler for sharemetadata");
this.splPath = splPath;
this.devListPath = devListPath;
this.masterPubKey = masterPubKey;
}
@Override
public void init(ShareMetaData smd) throws InitializaionException,
SignatureException {
SQLiteDatabase dbSPL = SQLiteDatabase.openOrCreateDatabase(splPath,
null);
SQLiteDatabase dbDL = SQLiteDatabase.openOrCreateDatabase(devListPath,
null);
try {
dbSPL.beginTransactionNonExclusive();
SQLiteStatement s = dbSPL.compileStatement(SPL_HAS_TABLES);
int tables = (int) s.simpleQueryForLong();
s.close();
if (SPL_NUM_TABLES == tables) {
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"Tables exist, loading values...");
loadSPLValues(dbSPL, smd);
// Load DeviceList
dbDL.beginTransaction();
s = dbDL.compileStatement(DEVICELIST_HAS_TABLES);
tables = (int) s.simpleQueryForLong();
s.close();
if (DEVICELIST_NUM_TABLES == tables) {
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"DeviceList exists, loading values...");
loadDeviceList(dbDL, smd);
}
dbDL.setTransactionSuccessful();
}
dbSPL.setTransactionSuccessful();
} finally {
dbSPL.endTransaction();
dbSPL.close();
dbDL.endTransaction();
dbDL.close();
}
}
private void loadSharePaticipants(SQLiteDatabase dbSPL, ShareMetaData smd)
throws InitializaionException, SignatureException {
smd.shareParticipants = new SharePartList();
// Load Publickeys of participants
Cursor cursor = dbSPL.rawQuery(QUERY_SPL, null);
try {
while (cursor.moveToNext()) {
PublicKey key = CryptCore.createPublicKeyFromBytes(cursor
.getBlob(cursor.getColumnIndex(COL_PUB_KEY)));
smd.shareParticipants.add(key,
cursor.getString(cursor.getColumnIndex(COL_ALIAS)));
}
cursor.close();
// Load and verify signature
cursor = dbSPL.rawQuery(QUERY_SIGNATURE, null);
while (cursor.moveToNext()) {
byte[] signature = cursor.getBlob(cursor
.getColumnIndex(COL_SIGNATURE));
boolean verified = false;
try {
verified = SignatureHelper.verify(smd.shareParticipants,
signature, smd.ownerPubSigKey);
smd.shareParticipants.setSignature(signature);
} catch (Exception e) {
}
if (!verified) {
throw new SignatureException(
"ShareParticipantList signature could not be verified!");
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private void loadDeviceList(SQLiteDatabase dbDL, ShareMetaData smd)
throws SignatureException {
smd.deviceLists = new TreeMap<PublicKey, DeviceList>(
Utils.PK_COMPARATOR);
DeviceList list = smd.deviceLists.get(masterPubKey);
if (list == null) {
list = new DeviceList(masterPubKey, null);
smd.deviceLists.put(masterPubKey, list);
}
Cursor cursor = dbDL.rawQuery(QUERY_DEVICE_LIST, null);
while (cursor.moveToNext()) {
String device = cursor.getString(cursor
.getColumnIndex(COL_DEV_ALIAS));
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"Loading device: " + device);
PublicKey devicePubKey = CryptCore.createPublicKeyFromBytes(cursor
.getBlob(cursor.getColumnIndex(COL_DEV_PUB_KEY)));
list.addDevice(device, devicePubKey);
}
cursor.close();
loadKeyValues(dbDL, smd);
checkDeviceListSignature(dbDL, smd, list);
}
private void checkDeviceListSignature(SQLiteDatabase dbDL,
ShareMetaData smd, DeviceList list) throws SignatureException {
// Check Signature
if (list != null) {
Collection<PublicKey> pKeys = list.getPublicKeys();
byte[] signature = getDeviceListSignature(dbDL);
if (signature == null) {
Log.e(AndroidJDBCHelperNonRevokeable.class.getName(),
"No signature for devicelist found");
throw new SignatureException(
"No signature for devicelist found");
}
boolean verified = false;
try {
// Either signed by the device list owner or by the
// shareOwner
Signable sKeys = smd.shareKeys.get(pKeys);
Signable obKeys = smd.obfuscationKeys.get(pKeys);
verified = SignatureHelper.verify(signature, masterPubKey,
list, sKeys, obKeys);
if (!verified) {
verified = SignatureHelper.verify(signature,
smd.ownerPubSigKey, list, sKeys, obKeys);
}
} catch (Exception e) {
throw new SignatureException("Could not verify signature", e);
}
if (!verified) {
Log.e(AndroidJDBCHelperNonRevokeable.class.getName(),
"Could not verify devicelist");
throw new SignatureException("Could not verify devicelist");
}
list.setSignature(signature);
}
}
private byte[] getDeviceListSignature(SQLiteDatabase dbDL)
throws SignatureException {
Cursor cursor = dbDL.rawQuery(QUERY_SIGNATURE, null);
if (cursor.moveToNext()) {
byte[] signature = cursor.getBlob(cursor
.getColumnIndex(COL_SIGNATURE));
if (cursor.moveToNext()) {
Log.e(AndroidJDBCHelperNonRevokeable.class.getName(),
"More than one device list signature found");
cursor.close();
throw new SignatureException(
"More than one device list signature found");
}
cursor.close();
return signature;
} else {
cursor.close();
throw new SignatureException("No signature found for device list");
}
}
private void loadKeyValues(SQLiteDatabase dbDL, ShareMetaData smd) {
loadShareKeys(dbDL, smd);
loadObfuscationKeys(dbDL, smd);
}
private void loadObfuscationKeys(SQLiteDatabase dbDL, ShareMetaData smd) {
if (smd.obfuscationKeys == null) {
smd.obfuscationKeys = new ObfuscationKeyDB();
}
Cursor cursor = dbDL.rawQuery(QUERY_OBFUSCATION_KEYS, null);
while (cursor.moveToNext()) {
PublicKey key = CryptCore.createPublicKeyFromBytes(cursor
.getBlob(cursor.getColumnIndex(COL_DEV_PUB_KEY)));
byte[] encKey = cursor.getBlob(cursor.getColumnIndex(COL_ENC_KEY));
smd.obfuscationKeys.add(key, encKey);
}
cursor.close();
}
private void loadShareKeys(SQLiteDatabase dbDL, ShareMetaData smd) {
if (smd.shareKeys == null) {
smd.shareKeys = new ShareKeyDB();
}
Cursor cursor = dbDL.rawQuery(QUERY_SHARE_KEYS, null);
while (cursor.moveToNext()) {
PublicKey key = CryptCore.createPublicKeyFromBytes(cursor
.getBlob(cursor.getColumnIndex(COL_DEV_PUB_KEY)));
int keyID = cursor.getInt(cursor.getColumnIndex(COL_KEY_ID));
byte[] encKey = cursor.getBlob(cursor.getColumnIndex(COL_ENC_KEY));
ShareKeyDBEntry entry = smd.shareKeys.getEntry(keyID);
if (entry == null) {
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"Creating new ShareKeyEntry: " + keyID);
entry = new ShareKeyDBEntry(smd.shareKeyAlgorithm, keyID);
smd.shareKeys.add(keyID, entry);
}
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"Adding ShareKey to entry " + keyID);
entry.addEncryptedKey(encKey, key);
Log.d(AndroidJDBCHelperNonRevokeable.class.getName(),
"Number of keys in entry: " + entry.size());
}
cursor.close();
}
private void loadSPLValues(SQLiteDatabase dbSPL, ShareMetaData smd)
throws InitializaionException, SignatureException {
loadID(dbSPL, smd);
loadAlgorithms(dbSPL, smd);
loadSharePaticipants(dbSPL, smd);
}
private void loadAlgorithms(SQLiteDatabase db, ShareMetaData smd)
throws InitializaionException {
smd.publicKeyAlgorithm = loadMetadata(db, smd, KEY_PK_ALGO);
if (smd.publicKeyAlgorithm == null) {
smd.publicKeyAlgorithm = KeyConstants.ASYMMETRIC_ALGORITHM;
}
smd.shareKeyAlgorithm = loadMetadata(db, smd, KEY_SK_ALGO);
if (smd.shareKeyAlgorithm == null) {
smd.shareKeyAlgorithm = KeyConstants.SYMMETRIC_ALGORITHM;
}
smd.obfuscationKeyAlgorithm = loadMetadata(db, smd, KEY_OK_ALGO);
if (smd.obfuscationKeyAlgorithm == null) {
smd.obfuscationKeyAlgorithm = KeyConstants.SYMMETRIC_ALGORITHM;
}
}
private void loadID(SQLiteDatabase db, ShareMetaData smd)
throws InitializaionException {
smd.id = UUID.fromString(loadMetadata(db, smd, KEY_UUID));
}
private String loadMetadata(SQLiteDatabase db, ShareMetaData smd, String key)
throws InitializaionException {
String result = null;
Cursor cursor = db.rawQuery(QUERY_METADATA, new String[] { key });
int count = cursor.getCount();
if (count == 0) {
throw new InitializaionException("Could not find " + key
+ " in metadata database! DB might be corrupt.");
} else {
cursor.moveToFirst();
result = cursor.getString(cursor.getColumnIndex(COL_VALUE));
}
cursor.close();
return result;
}
@Override
public void store(DeviceList deviceList, ObfuscationKeyDB obKeys,
ShareKeyDB shareKeys) throws PersistanceException {
throw new RuntimeException("Not yet implemented");
}
@Override
public void storeSPL(ShareMetaData smd) throws PersistanceException {
throw new RuntimeException("Not yet implemented");
}
@Override
public void load(ShareMetaData smd) throws SQLException,
SignatureException, InitializaionException {
smd.shareParticipants = null;
smd.deviceLists.clear();
smd.deviceLists = null;
smd.shareKeys = null;
smd.obfuscationKeys = null;
init(smd);
}
@Override
public boolean exists() {
return new File(splPath).isFile() && new File(devListPath).isFile();
}
}