Use the new CryptoManager instead of the main encryption password.

This will give us more flexibility in the future because now the
encryption key and derivation is all managed in one place.
pull/5/head
Tom Hacohen 7 years ago
parent 4004eca762
commit d2eaf5f434

@ -73,19 +73,15 @@ abstract class BaseManager {
return uid;
}
public String getContent(String keyBase64) {
// FIXME: probably cache encryption object
Crypto.CryptoManager cryptoManager = new Crypto.CryptoManager(keyBase64, null);
return new String(cryptoManager.decrypt(content), Charsets.UTF_8);
public String getContent(Crypto.CryptoManager crypto) {
return new String(crypto.decrypt(content), Charsets.UTF_8);
}
void setContent(String keyBase64, String content) {
// FIXME: probably cache encryption object
Crypto.CryptoManager cryptoManager = new Crypto.CryptoManager(keyBase64, null);
this.content = cryptoManager.encrypt(content.getBytes(Charsets.UTF_8));
void setContent(Crypto.CryptoManager crypto, String content) {
this.content = crypto.encrypt(content.getBytes(Charsets.UTF_8));
}
byte[] calculateHmac(String keyBase64, String uuid) {
byte[] calculateHmac(Crypto.CryptoManager crypto, String uuid) {
ByteArrayOutputStream hashContent = new ByteArrayOutputStream();
try {
@ -99,16 +95,14 @@ abstract class BaseManager {
return "DEADBEEFDEADBEEFDEADBEEFDEADBEEF".getBytes();
}
// FIXME: probably cache encryption object
Crypto.CryptoManager cryptoManager = new Crypto.CryptoManager(keyBase64, null);
return cryptoManager.hmac(hashContent.toByteArray());
return crypto.hmac(hashContent.toByteArray());
}
protected Base() {
}
Base(String keyBase64, String content, String uid) {
setContent(keyBase64, content);
Base(Crypto.CryptoManager crypto, String content, String uid) {
setContent(crypto, content);
setUid(uid);
}

@ -31,12 +31,12 @@ public class Crypto {
return Base64.encodeToString(SCrypt.generate(password.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8), 16384, 8, 1, keySize), Base64.NO_WRAP);
}
static class CryptoManager {
public static class CryptoManager {
private SecureRandom _random = null;
private final byte[] cipherKey;
private final byte[] hmacKey;
CryptoManager(String keyBase64, String salt) {
public CryptoManager(String keyBase64, String salt) {
byte[] derivedKey; // FIXME use salt = hmac256(salt.getBytes(Charsets.UTF_8), Base64.decode(keyBase64, Base64.NO_WRAP));
derivedKey = Base64.decode(keyBase64, Base64.NO_WRAP);
cipherKey = hmac256("aes".getBytes(Charsets.UTF_8), derivedKey);

@ -35,7 +35,7 @@ public class JournalEntryManager extends BaseManager {
this.client = httpClient;
}
public List<Entry> getEntries(String keyBase64, String last) throws Exceptions.HttpException, Exceptions.IntegrityException {
public List<Entry> getEntries(Crypto.CryptoManager crypto, String last) throws Exceptions.HttpException, Exceptions.IntegrityException {
Entry previousEntry = null;
HttpUrl.Builder urlBuilder = this.remote.newBuilder();
if (last != null) {
@ -55,7 +55,7 @@ public class JournalEntryManager extends BaseManager {
List<Entry> ret = GsonHelper.gson.fromJson(body.charStream(), entryType);
for (Entry entry : ret) {
entry.verify(keyBase64, previousEntry);
entry.verify(crypto, previousEntry);
previousEntry = entry;
}
@ -85,13 +85,13 @@ public class JournalEntryManager extends BaseManager {
super();
}
public void update(String keyBase64, String content, Entry previous) {
setContent(keyBase64, content);
setUid(calculateHmac(keyBase64, previous));
public void update(Crypto.CryptoManager crypto, String content, Entry previous) {
setContent(crypto, content);
setUid(calculateHmac(crypto, previous));
}
void verify(String keyBase64, Entry previous) throws Exceptions.IntegrityException {
String correctHash = calculateHmac(keyBase64, previous);
void verify(Crypto.CryptoManager crypto, Entry previous) throws Exceptions.IntegrityException {
String correctHash = calculateHmac(crypto, previous);
if (!getUuid().equals(correctHash)) {
throw new Exceptions.IntegrityException("Bad HMAC. " + getUuid() + " != " + correctHash);
}
@ -103,13 +103,13 @@ public class JournalEntryManager extends BaseManager {
return ret;
}
private String calculateHmac(String keyBase64, Entry previous) {
private String calculateHmac(Crypto.CryptoManager crypto, Entry previous) {
String uuid = null;
if (previous != null) {
uuid = previous.getUuid();
}
return Crypto.toHex(calculateHmac(keyBase64, uuid));
return Crypto.toHex(calculateHmac(crypto, uuid));
}
}

@ -46,8 +46,9 @@ public class JournalManager extends BaseManager {
List<Journal> ret = GsonHelper.gson.fromJson(body.charStream(), journalType);
for (Journal journal : ret) {
Crypto.CryptoManager crypto = new Crypto.CryptoManager(keyBase64, journal.getUuid());
journal.processFromJson();
journal.verify(keyBase64);
journal.verify(crypto);
}
return ret;
@ -95,13 +96,9 @@ public class JournalManager extends BaseManager {
super();
}
public Journal(String keyBase64, String content) {
this(keyBase64, content, sha256(UUID.randomUUID().toString()));
}
public Journal(String keyBase64, String content, String uid) {
super(keyBase64, content, uid);
hmac = calculateHmac(keyBase64);
public Journal(Crypto.CryptoManager crypto, String content, String uid) {
super(crypto, content, uid);
hmac = calculateHmac(crypto);
}
private void processFromJson() {
@ -109,19 +106,23 @@ public class JournalManager extends BaseManager {
setContent(Arrays.copyOfRange(getContent(), hmacSize, getContent().length));
}
void verify(String keyBase64) throws Exceptions.IntegrityException {
void verify(Crypto.CryptoManager crypto) throws Exceptions.IntegrityException {
if (hmac == null) {
throw new Exceptions.IntegrityException("HMAC is null!");
}
byte[] correctHash = calculateHmac(keyBase64);
byte[] correctHash = calculateHmac(crypto);
if (!Arrays.areEqual(hmac, correctHash)) {
throw new Exceptions.IntegrityException("Bad HMAC. " + toHex(hmac) + " != " + toHex(correctHash));
}
}
byte[] calculateHmac(String keyBase64) {
return super.calculateHmac(keyBase64, getUuid());
byte[] calculateHmac(Crypto.CryptoManager crypto) {
return super.calculateHmac(crypto, getUuid());
}
public static String genUid() {
return sha256(UUID.randomUUID().toString());
}
@Override

@ -1,6 +1,7 @@
package com.etesync.syncadapter.model;
import com.etesync.syncadapter.GsonHelper;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.JournalEntryManager;
import lombok.Getter;
@ -41,8 +42,8 @@ public class SyncEntry {
return this.action.equals(action);
}
public static SyncEntry fromJournalEntry(String keyBase64, JournalEntryManager.Entry entry) {
return fromJson(entry.getContent(keyBase64));
public static SyncEntry fromJournalEntry(Crypto.CryptoManager crypto, JournalEntryManager.Entry entry) {
return fromJson(entry.getContent(crypto));
}
static SyncEntry fromJson(String json) {

@ -45,6 +45,7 @@ import com.etesync.syncadapter.Constants;
import com.etesync.syncadapter.HttpClient;
import com.etesync.syncadapter.InvalidAccountException;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalManager;
import com.etesync.syncadapter.model.CollectionInfo;
@ -160,7 +161,8 @@ public abstract class SyncAdapterService extends Service {
List<CollectionInfo> collections = new LinkedList<>();
for (JournalManager.Journal journal : journalsManager.getJournals(settings.password())) {
CollectionInfo info = CollectionInfo.fromJson(journal.getContent(settings.password()));
Crypto.CryptoManager crypto = new Crypto.CryptoManager(settings.password(), journal.getUuid());
CollectionInfo info = CollectionInfo.fromJson(journal.getContent(crypto));
info.url = journal.getUuid();
if (info.type.equals(serviceType)) {
collections.add(info);
@ -169,9 +171,10 @@ public abstract class SyncAdapterService extends Service {
if (collections.isEmpty()) {
CollectionInfo info = CollectionInfo.defaultForServiceType(serviceType);
JournalManager.Journal journal = new JournalManager.Journal(settings.password(), info.toJson());
info.url = JournalManager.Journal.genUid();
Crypto.CryptoManager crypto = new Crypto.CryptoManager(settings.password(), info.url);
JournalManager.Journal journal = new JournalManager.Journal(crypto, info.toJson(), info.url);
journalsManager.putJournal(journal);
info.url = journal.getUuid();
collections.add(info);
}

@ -21,6 +21,7 @@ import com.etesync.syncadapter.HttpClient;
import com.etesync.syncadapter.InvalidAccountException;
import com.etesync.syncadapter.NotificationHelper;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalEntryManager;
import com.etesync.syncadapter.model.CollectionInfo;
@ -69,6 +70,8 @@ abstract public class SyncManager {
protected JournalEntryManager journal;
private JournalEntity _journalEntity;
private final Crypto.CryptoManager crypto;
private EntityDataStore<Persistable> data;
/**
@ -109,6 +112,8 @@ abstract public class SyncManager {
notificationManager = new NotificationHelper(context, journalUid, notificationId());
notificationManager.cancel();
crypto = new Crypto.CryptoManager(settings.password(), journalUid);
data = ((App) context.getApplicationContext()).getData();
}
@ -249,7 +254,7 @@ abstract public class SyncManager {
i++;
App.log.info("Processing (" + String.valueOf(i) + "/" + strTotal + ") " + entry.toString());
SyncEntry cEntry = SyncEntry.fromJournalEntry(settings.password(), entry);
SyncEntry cEntry = SyncEntry.fromJournalEntry(crypto, entry);
persistSyncEntry(entry.getUuid(), cEntry);
if (cEntry.isAction(SyncEntry.Actions.DELETE)) {
continue;
@ -266,10 +271,10 @@ abstract public class SyncManager {
int count = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(getJournalEntity())).get().value();
if ((remoteCTag != null) && (count == 0)) {
// If we are updating an existing installation with no saved journal, we need to add
remoteEntries = journal.getEntries(settings.password(), null);
remoteEntries = journal.getEntries(crypto, null);
int i = 0;
for (JournalEntryManager.Entry entry : remoteEntries) {
SyncEntry cEntry = SyncEntry.fromJournalEntry(settings.password(), entry);
SyncEntry cEntry = SyncEntry.fromJournalEntry(crypto, entry);
persistSyncEntry(entry.getUuid(), cEntry);
i++;
if (remoteCTag.equals(entry.getUuid())) {
@ -278,7 +283,7 @@ abstract public class SyncManager {
}
}
} else {
remoteEntries = journal.getEntries(settings.password(), remoteCTag);
remoteEntries = journal.getEntries(crypto, remoteCTag);
}
App.log.info("Fetched " + String.valueOf(remoteEntries.size()) + " entries");
@ -297,7 +302,7 @@ abstract public class SyncManager {
i++;
App.log.info("Processing (" + String.valueOf(i) + "/" + strTotal + ") " + entry.toString());
SyncEntry cEntry = SyncEntry.fromJournalEntry(settings.password(), entry);
SyncEntry cEntry = SyncEntry.fromJournalEntry(crypto, entry);
App.log.info("Processing resource for journal entry");
processSyncEntry(cEntry);
@ -360,7 +365,7 @@ abstract public class SyncManager {
for (LocalResource local : localDeleted) {
SyncEntry entry = new SyncEntry(local.getContent(), SyncEntry.Actions.DELETE);
JournalEntryManager.Entry tmp = new JournalEntryManager.Entry();
tmp.update(settings.password(), entry.toJson(), previousEntry);
tmp.update(crypto, entry.toJson(), previousEntry);
previousEntry = tmp;
localEntries.add(previousEntry);
}
@ -375,7 +380,7 @@ abstract public class SyncManager {
SyncEntry entry = new SyncEntry(local.getContent(), action);
JournalEntryManager.Entry tmp = new JournalEntryManager.Entry();
tmp.update(settings.password(), entry.toJson(), previousEntry);
tmp.update(crypto, entry.toJson(), previousEntry);
previousEntry = tmp;
localEntries.add(previousEntry);
}

@ -31,6 +31,7 @@ import com.etesync.syncadapter.App;
import com.etesync.syncadapter.HttpClient;
import com.etesync.syncadapter.InvalidAccountException;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalManager;
import com.etesync.syncadapter.model.CollectionInfo;
@ -156,12 +157,13 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa
JournalManager journalManager = new JournalManager(HttpClient.create(getContext(), account), principal);
if (info.url == null) {
// CollectionInfo doesn't have a url at this point, update it.
JournalManager.Journal journal = new JournalManager.Journal(settings.password(), info.toJson());
info.url = JournalManager.Journal.genUid();
Crypto.CryptoManager crypto = new Crypto.CryptoManager(settings.password(), info.url);
JournalManager.Journal journal = new JournalManager.Journal(crypto, info.toJson(), info.url);
journalManager.putJournal(journal);
info.url = journal.getUuid();
} else {
JournalManager.Journal journal = new JournalManager.Journal(settings.password(), info.toJson(), info.url);
Crypto.CryptoManager crypto = new Crypto.CryptoManager(settings.password(), info.url);
JournalManager.Journal journal = new JournalManager.Journal(crypto, info.toJson(), info.url);
journalManager.updateJournal(journal);
}

@ -28,6 +28,7 @@ import com.etesync.syncadapter.App;
import com.etesync.syncadapter.HttpClient;
import com.etesync.syncadapter.InvalidAccountException;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalManager;
import com.etesync.syncadapter.model.CollectionInfo;
@ -119,7 +120,9 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa
HttpUrl principal = HttpUrl.get(settings.getUri());
JournalManager journalManager = new JournalManager(HttpClient.create(getContext(), account), principal);
journalManager.deleteJournal(new JournalManager.Journal(settings.password(), collectionInfo.toJson(), collectionInfo.url));
Crypto.CryptoManager crypto = new Crypto.CryptoManager(settings.password(), collectionInfo.url);
journalManager.deleteJournal(new JournalManager.Journal(crypto, collectionInfo.toJson(), collectionInfo.url));
JournalEntity journalEntity = JournalEntity.fetch(data, collectionInfo.url);
journalEntity.setDeleted(true);
data.update(journalEntity);

Loading…
Cancel
Save