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
pull/14/head
Tom Hacohen 7 years ago
parent e836b4c716
commit 11e37dbd1e

@ -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;

@ -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());
}
}
}

@ -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);
}
}

Loading…
Cancel
Save