You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/app/src/main/java/at/bitfire/davdroid/journalmanager/JournalManager.java

125 lines
3.8 KiB

package at.bitfire.davdroid.journalmanager;
import com.google.gson.reflect.TypeToken;
import org.spongycastle.util.Arrays;
import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;
import aQute.lib.hex.Hex;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.GsonHelper;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import static at.bitfire.davdroid.journalmanager.Helpers.sha256;
public class JournalManager extends BaseManager {
final static private Type journalType = new TypeToken<List<Journal>>() {
}.getType();
public JournalManager(OkHttpClient httpClient, HttpUrl remote) {
this.remote = remote.newBuilder()
.addPathSegments("api/v1/journals")
.addPathSegment("")
.build();
App.log.info("Created for: " + this.remote.toString());
this.client = httpClient;
}
public List<Journal> getJournals(String keyBase64) throws Exceptions.HttpException, Exceptions.IntegrityException {
Request request = new Request.Builder()
.get()
.url(remote)
.build();
Response response = newCall(request);
ResponseBody body = response.body();
List<Journal> ret = GsonHelper.gson.fromJson(body.charStream(), journalType);
for (Journal journal : ret) {
journal.processFromJson();
journal.verify(keyBase64);
}
return ret;
}
public void deleteJournal(Journal journal) throws Exceptions.HttpException {
HttpUrl remote = this.remote.resolve(journal.getUuid() + "/");
Request request = new Request.Builder()
.delete()
.url(remote)
.build();
newCall(request);
}
public void putJournal(Journal journal) throws Exceptions.HttpException {
RequestBody body = RequestBody.create(JSON, journal.toJson());
Request request = new Request.Builder()
.post(body)
.url(remote)
.build();
newCall(request);
}
public static class Journal extends Base {
final private transient int hmacSize = 256 / 8; // hmac256 in bytes
private transient byte[] hmac = null;
@SuppressWarnings("unused")
private Journal() {
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);
}
private void processFromJson() {
hmac = Arrays.copyOfRange(getContent(), 0, hmacSize);
setContent(Arrays.copyOfRange(getContent(), hmacSize, getContent().length));
}
void verify(String keyBase64) throws Exceptions.IntegrityException {
if (hmac == null) {
throw new Exceptions.IntegrityException("HMAC is null!");
}
byte[] correctHash = calculateHmac(keyBase64);
if (!Arrays.areEqual(hmac, correctHash)) {
throw new Exceptions.IntegrityException("Bad HMAC. " + Hex.toHexString(hmac) + " != " + Hex.toHexString(correctHash));
}
}
byte[] calculateHmac(String keyBase64) {
return super.calculateHmac(keyBase64, getUuid());
}
@Override
String toJson() {
byte[] rawContent = getContent();
setContent(Arrays.concatenate(hmac, rawContent));
String ret = super.toJson();
setContent(rawContent);
return ret;
}
}
}