diff --git a/app/src/main/java/com/etesync/syncadapter/journalmanager/BaseManager.java b/app/src/main/java/com/etesync/syncadapter/journalmanager/BaseManager.java index 8e7179d3..b1f1bed9 100644 --- a/app/src/main/java/com/etesync/syncadapter/journalmanager/BaseManager.java +++ b/app/src/main/java/com/etesync/syncadapter/journalmanager/BaseManager.java @@ -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); } diff --git a/app/src/main/java/com/etesync/syncadapter/journalmanager/Crypto.java b/app/src/main/java/com/etesync/syncadapter/journalmanager/Crypto.java index fc3bf0b6..d2c5581e 100644 --- a/app/src/main/java/com/etesync/syncadapter/journalmanager/Crypto.java +++ b/app/src/main/java/com/etesync/syncadapter/journalmanager/Crypto.java @@ -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); diff --git a/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalEntryManager.java b/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalEntryManager.java index 18d4389d..ae2e992d 100644 --- a/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalEntryManager.java +++ b/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalEntryManager.java @@ -35,7 +35,7 @@ public class JournalEntryManager extends BaseManager { this.client = httpClient; } - public List getEntries(String keyBase64, String last) throws Exceptions.HttpException, Exceptions.IntegrityException { + public List 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 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)); } } diff --git a/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalManager.java b/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalManager.java index 80d2d5a9..9110124f 100644 --- a/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalManager.java +++ b/app/src/main/java/com/etesync/syncadapter/journalmanager/JournalManager.java @@ -46,8 +46,9 @@ public class JournalManager extends BaseManager { List 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 diff --git a/app/src/main/java/com/etesync/syncadapter/model/SyncEntry.java b/app/src/main/java/com/etesync/syncadapter/model/SyncEntry.java index 98368f96..449d7a04 100644 --- a/app/src/main/java/com/etesync/syncadapter/model/SyncEntry.java +++ b/app/src/main/java/com/etesync/syncadapter/model/SyncEntry.java @@ -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) { diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java index 694b5481..0b1dfd6b 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java @@ -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 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); } diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java index 2a586e18..6cca5f1a 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java @@ -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 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); } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java index 20686255..4adefa27 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java @@ -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); } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java index 20d50926..0073a994 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java @@ -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);