Support for versioned profiles

Includes support for issuing zkgroup auth credentials
This commit is contained in:
Moxie Marlinspike
2019-10-09 11:30:01 -07:00
parent a94fc22659
commit ba3102d667
23 changed files with 1315 additions and 98 deletions

View File

@@ -0,0 +1,55 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.hibernate.validator.constraints.NotEmpty;
import org.signal.zkgroup.profiles.ProfileKeyCommitment;
import org.whispersystems.textsecuregcm.util.ExactlySize;
import javax.validation.constraints.NotNull;
public class CreateProfileRequest {
@JsonProperty
@NotEmpty
private String version;
@JsonProperty
@ExactlySize({108})
private String name;
@JsonProperty
private boolean avatar;
@JsonProperty
@NotNull
@JsonDeserialize(using = ProfileKeyCommitmentAdapter.Deserializing.class)
@JsonSerialize(using = ProfileKeyCommitmentAdapter.Serializing.class)
private ProfileKeyCommitment commitment;
public CreateProfileRequest() {}
public CreateProfileRequest(ProfileKeyCommitment commitment, String version, String name, boolean wantsAvatar) {
this.commitment = commitment;
this.version = version;
this.name = name;
this.avatar = wantsAvatar;
}
public ProfileKeyCommitment getCommitment() {
return commitment;
}
public String getVersion() {
return version;
}
public String getName() {
return name;
}
public boolean isAvatar() {
return avatar;
}
}

View File

@@ -0,0 +1,72 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.whispersystems.textsecuregcm.util.Base64;
import java.io.IOException;
import java.util.List;
public class GroupCredentials {
@JsonProperty
private List<GroupCredential> credentials;
public GroupCredentials() {}
public GroupCredentials(List<GroupCredential> credentials) {
this.credentials = credentials;
}
public List<GroupCredential> getCredentials() {
return credentials;
}
public static class GroupCredential {
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] credential;
@JsonProperty
private int redemptionTime;
public GroupCredential() {}
public GroupCredential(byte[] credential, int redemptionTime) {
this.credential = credential;
this.redemptionTime = redemptionTime;
}
public byte[] getCredential() {
return credential;
}
public int getRedemptionTime() {
return redemptionTime;
}
}
public static class ByteArraySerializer extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] bytes, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(Base64.encodeBytes(bytes));
}
}
public static class ByteArrayDeserializer extends JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
return Base64.decode(jsonParser.getValueAsString());
}
}
}

View File

@@ -1,8 +1,12 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
import java.util.UUID;
public class Profile {
@@ -31,11 +35,17 @@ public class Profile {
@JsonProperty
private UUID uuid;
@JsonProperty
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
@JsonDeserialize(using = ProfileKeyCredentialResponseAdapter.Deserializing.class)
private ProfileKeyCredentialResponse credential;
public Profile() {}
public Profile(String name, String avatar, String identityKey,
String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess,
UserCapabilities capabilities, String username, UUID uuid)
UserCapabilities capabilities, String username, UUID uuid,
ProfileKeyCredentialResponse credential)
{
this.name = name;
this.avatar = avatar;
@@ -45,6 +55,7 @@ public class Profile {
this.capabilities = capabilities;
this.username = username;
this.uuid = uuid;
this.credential = credential;
}
@VisibleForTesting

View File

@@ -41,4 +41,8 @@ public class ProfileAvatarUploadAttributes {
this.signature = signature;
}
public String getKey() {
return key;
}
}

View File

@@ -0,0 +1,37 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKeyCommitment;
import org.whispersystems.textsecuregcm.util.Base64;
import java.io.IOException;
public class ProfileKeyCommitmentAdapter {
public static class Serializing extends JsonSerializer<ProfileKeyCommitment> {
@Override
public void serialize(ProfileKeyCommitment value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(Base64.encodeBytes(value.serialize()));
}
}
public static class Deserializing extends JsonDeserializer<ProfileKeyCommitment> {
@Override
public ProfileKeyCommitment deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return new ProfileKeyCommitment(Base64.decode(p.getValueAsString()));
} catch (InvalidInputException e) {
throw new IOException(e);
}
}
}
}

View File

@@ -0,0 +1,40 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
import org.whispersystems.textsecuregcm.util.Base64;
import java.io.IOException;
public class ProfileKeyCredentialResponseAdapter {
public static class Serializing extends JsonSerializer<ProfileKeyCredentialResponse> {
@Override
public void serialize(ProfileKeyCredentialResponse response, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException, JsonProcessingException
{
if (response == null) jsonGenerator.writeNull();
else jsonGenerator.writeString(Base64.encodeBytes(response.serialize()));
}
}
public static class Deserializing extends JsonDeserializer<ProfileKeyCredentialResponse> {
@Override
public ProfileKeyCredentialResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException
{
try {
return new ProfileKeyCredentialResponse(Base64.decode(jsonParser.getValueAsString()));
} catch (InvalidInputException e) {
throw new IOException(e);
}
}
}
}