1
0
mirror of https://github.com/etesync/android synced 2025-01-11 00:01:12 +00:00

Journalmanager: add API to interact with the UserInfo

This is where the keypair is stored on the server. Both the public
facing public key, and the encrypted private key
This commit is contained in:
Tom Hacohen 2017-04-12 11:54:41 +01:00
parent e836b4c716
commit 11e37dbd1e
3 changed files with 189 additions and 3 deletions

View File

@ -1,16 +1,16 @@
package com.etesync.syncadapter.journalmanager;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.GsonHelper;
import com.google.gson.reflect.TypeToken;
import org.spongycastle.util.Arrays;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.GsonHelper;
import lombok.Getter;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;

View File

@ -0,0 +1,150 @@
package com.etesync.syncadapter.journalmanager;
import com.etesync.syncadapter.GsonHelper;
import org.apache.commons.codec.Charsets;
import org.spongycastle.util.Arrays;
import java.io.IOException;
import java.net.HttpURLConnection;
import lombok.Getter;
import lombok.Setter;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import static com.etesync.syncadapter.journalmanager.Crypto.CryptoManager.HMAC_SIZE;
import static com.etesync.syncadapter.journalmanager.Crypto.toHex;
public class UserInfoManager extends BaseManager {
public UserInfoManager(OkHttpClient httpClient, HttpUrl remote) {
this.remote = remote.newBuilder()
.addPathSegments("api/v1/user")
.addPathSegment("")
.build();
this.client = httpClient;
}
public UserInfo get(Crypto.CryptoManager cryptoManager, String owner) throws Exceptions.HttpException, Exceptions.IntegrityException, Exceptions.GenericCryptoException {
HttpUrl remote = this.remote.newBuilder().addPathSegment(owner).addPathSegment("").build();
Request request = new Request.Builder()
.get()
.url(remote)
.build();
Response response;
try {
response = newCall(request);
} catch (Exceptions.HttpException e) {
if (e.status == HttpURLConnection.HTTP_NOT_FOUND) {
return null;
} else {
throw e;
}
}
ResponseBody body = response.body();
UserInfo ret = GsonHelper.gson.fromJson(body.charStream(), UserInfo.class);
ret.verify(cryptoManager);
ret.setOwner(owner);
return ret;
}
public void delete(UserInfo userInfo) throws Exceptions.HttpException {
HttpUrl remote = this.remote.newBuilder().addPathSegment(userInfo.getOwner()).addPathSegment("").build();
Request request = new Request.Builder()
.delete()
.url(remote)
.build();
newCall(request);
}
public void create(UserInfo userInfo) throws Exceptions.HttpException {
RequestBody body = RequestBody.create(JSON, userInfo.toJson());
Request request = new Request.Builder()
.post(body)
.url(remote)
.build();
newCall(request);
}
public void update(UserInfo userInfo) throws Exceptions.HttpException {
HttpUrl remote = this.remote.newBuilder().addPathSegment(userInfo.getOwner()).addPathSegment("").build();
RequestBody body = RequestBody.create(JSON, userInfo.toJson());
Request request = new Request.Builder()
.put(body)
.url(remote)
.build();
newCall(request);
}
public static class UserInfo {
@Setter
@Getter
private transient String owner;
@Getter
private byte version;
@Getter
private byte[] pubkey;
private byte[] content;
public byte[] getContent(Crypto.CryptoManager crypto) {
byte[] content = Arrays.copyOfRange(this.content, HMAC_SIZE, this.content.length);
return crypto.decrypt(content);
}
void setContent(Crypto.CryptoManager crypto, byte[] rawContent) {
byte[] content = crypto.encrypt(rawContent);
this.content = Arrays.concatenate(calculateHmac(crypto, content), content);
}
void verify(Crypto.CryptoManager crypto) throws Exceptions.IntegrityException {
if (this.content == null) {
// Nothing to verify.
return;
}
byte[] hmac = Arrays.copyOfRange(this.content, 0, HMAC_SIZE);
byte[] content = Arrays.copyOfRange(this.content, HMAC_SIZE, this.content.length);
byte[] correctHash = calculateHmac(crypto, content);
if (!Arrays.areEqual(hmac, correctHash)) {
throw new Exceptions.IntegrityException("Bad HMAC. " + toHex(hmac) + " != " + toHex(correctHash));
}
}
private byte[] calculateHmac(Crypto.CryptoManager crypto, byte[] content) {
return crypto.hmac(Arrays.concatenate(content, pubkey));
}
private UserInfo() {
}
public UserInfo(Crypto.CryptoManager crypto, String owner, byte[] pubkey, byte[] content) {
this.owner = owner;
this.pubkey = pubkey;
version = crypto.getVersion();
setContent(crypto, content);
}
public static UserInfo generate(Crypto.CryptoManager cryptoManager, String owner) throws IOException {
Crypto.AsymmetricKeyPair keyPair = Crypto.generateKeyPair();
return new UserInfo(cryptoManager, owner, keyPair.getPublicKey(), keyPair.getPrivateKey());
}
String toJson() {
return GsonHelper.gson.toJson(this, getClass());
}
}
}

View File

@ -12,6 +12,7 @@ import com.etesync.syncadapter.App;
import com.etesync.syncadapter.HttpClient;
import com.etesync.syncadapter.model.CollectionInfo;
import org.apache.commons.codec.Charsets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -30,7 +31,9 @@ import okio.BufferedSink;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class ServiceTest {
private OkHttpClient httpClient;
@ -196,4 +199,37 @@ public class ServiceTest {
}
assertNotNull(caught);
}
@Test
public void testUserInfo() throws IOException, Exceptions.HttpException, Exceptions.GenericCryptoException, Exceptions.IntegrityException {
Crypto.CryptoManager cryptoManager = new Crypto.CryptoManager(Constants.CURRENT_VERSION, Helpers.keyBase64, "userInfo");
UserInfoManager.UserInfo userInfo, userInfo2;
UserInfoManager manager = new UserInfoManager(httpClient, remote);
// Get when there's nothing
userInfo = manager.get(cryptoManager, Helpers.USER);
assertNull(userInfo);
// Create
userInfo = UserInfoManager.UserInfo.generate(cryptoManager, Helpers.USER);
manager.create(userInfo);
// Get
userInfo2 = manager.get(cryptoManager, Helpers.USER);
assertNotNull(userInfo2);
assertArrayEquals(userInfo.getContent(cryptoManager), userInfo2.getContent(cryptoManager));
// Update
userInfo.setContent(cryptoManager, "test".getBytes(Charsets.UTF_8));
manager.update(userInfo);
userInfo2 = manager.get(cryptoManager, Helpers.USER);
assertNotNull(userInfo2);
assertArrayEquals(userInfo.getContent(cryptoManager), userInfo2.getContent(cryptoManager));
// Delete
manager.delete(userInfo);
userInfo = manager.get(cryptoManager, Helpers.USER);
assertNull(userInfo);
}
}