From d2eaf5f4344a449660a0b9ed6e9f3629b44c8bf1 Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Wed, 29 Mar 2017 12:54:20 +0100 Subject: [PATCH] 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. --- .../journalmanager/BaseManager.java | 22 ++++++---------- .../syncadapter/journalmanager/Crypto.java | 4 +-- .../journalmanager/JournalEntryManager.java | 18 ++++++------- .../journalmanager/JournalManager.java | 25 ++++++++++--------- .../etesync/syncadapter/model/SyncEntry.java | 5 ++-- .../syncadapter/SyncAdapterService.java | 9 ++++--- .../syncadapter/syncadapter/SyncManager.java | 19 ++++++++------ .../ui/CreateCollectionFragment.java | 10 +++++--- .../ui/DeleteCollectionFragment.java | 5 +++- 9 files changed, 63 insertions(+), 54 deletions(-) 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);