mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 19:28:05 +01:00
Represent pre-key public keys and signatures as byte arrays in DAOs
This commit is contained in:
committed by
Jon Chambers
parent
4a8ad3103c
commit
217b68a1e0
@@ -6,8 +6,14 @@
|
||||
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.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PreKey {
|
||||
|
||||
@@ -16,22 +22,24 @@ public class PreKey {
|
||||
private long keyId;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotEmpty
|
||||
private String publicKey;
|
||||
private byte[] publicKey;
|
||||
|
||||
public PreKey() {}
|
||||
|
||||
public PreKey(long keyId, String publicKey)
|
||||
public PreKey(long keyId, byte[] publicKey)
|
||||
{
|
||||
this.keyId = keyId;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(String publicKey) {
|
||||
public void setPublicKey(byte[] publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@@ -44,23 +52,17 @@ public class PreKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || !(object instanceof PreKey)) return false;
|
||||
PreKey that = (PreKey)object;
|
||||
|
||||
if (publicKey == null) {
|
||||
return this.keyId == that.keyId && that.publicKey == null;
|
||||
} else {
|
||||
return this.keyId == that.keyId && this.publicKey.equals(that.publicKey);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PreKey preKey = (PreKey) o;
|
||||
return keyId == preKey.keyId && Arrays.equals(publicKey, preKey.publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (publicKey == null) {
|
||||
return (int)this.keyId;
|
||||
} else {
|
||||
return ((int)this.keyId) ^ publicKey.hashCode();
|
||||
}
|
||||
int result = Objects.hash(keyId);
|
||||
result = 31 * result + Arrays.hashCode(publicKey);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,13 @@ public abstract class PreKeySignatureValidator {
|
||||
public static final Counter INVALID_SIGNATURE_COUNTER =
|
||||
Metrics.counter(name(PreKeySignatureValidator.class, "invalidPreKeySignature"));
|
||||
|
||||
public static final boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
||||
public static boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
||||
try {
|
||||
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
|
||||
final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0);
|
||||
|
||||
final boolean success = spks.stream().allMatch(spk -> {
|
||||
final byte[] prekeyBytes = Base64.getDecoder().decode(spk.getPublicKey());
|
||||
final byte[] prekeySignatureBytes = Base64.getDecoder().decode(spk.getSignature());
|
||||
|
||||
return identityKey.verifySignature(prekeyBytes, prekeySignatureBytes);
|
||||
});
|
||||
final boolean success = spks.stream()
|
||||
.allMatch(spk -> identityKey.verifySignature(spk.getPublicKey(), spk.getSignature()));
|
||||
|
||||
if (!success) {
|
||||
INVALID_SIGNATURE_COUNTER.increment();
|
||||
|
||||
@@ -6,43 +6,45 @@
|
||||
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.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SignedPreKey extends PreKey {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotEmpty
|
||||
private String signature;
|
||||
private byte[] signature;
|
||||
|
||||
public SignedPreKey() {}
|
||||
|
||||
public SignedPreKey(long keyId, String publicKey, String signature) {
|
||||
public SignedPreKey(long keyId, byte[] publicKey, byte[] signature) {
|
||||
super(keyId, publicKey);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || !(object instanceof SignedPreKey)) return false;
|
||||
SignedPreKey that = (SignedPreKey) object;
|
||||
|
||||
if (signature == null) {
|
||||
return super.equals(object) && that.signature == null;
|
||||
} else {
|
||||
return super.equals(object) && this.signature.equals(that.signature);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
SignedPreKey that = (SignedPreKey) o;
|
||||
return Arrays.equals(signature, that.signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (signature == null) {
|
||||
return super.hashCode();
|
||||
} else {
|
||||
return super.hashCode() ^ signature.hashCode();
|
||||
}
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(signature);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,24 +374,23 @@ public class Keys extends AbstractDynamoDbStore {
|
||||
return Map.of(
|
||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, spk.getKeyId()),
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getPublicKey())),
|
||||
KEY_SIGNATURE, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getSignature())));
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(spk.getPublicKey()),
|
||||
KEY_SIGNATURE, AttributeValues.fromByteArray(spk.getSignature()));
|
||||
}
|
||||
return Map.of(
|
||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, preKey.getKeyId()),
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(Base64.getDecoder().decode(preKey.getPublicKey())));
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(preKey.getPublicKey()));
|
||||
}
|
||||
|
||||
private PreKey getPreKeyFromItem(Map<String, AttributeValue> item) {
|
||||
final long keyId = item.get(KEY_DEVICE_ID_KEY_ID).b().asByteBuffer().getLong(8);
|
||||
final String publicKey = Base64.getEncoder().encodeToString(extractByteArray(item.get(KEY_PUBLIC_KEY)));
|
||||
final byte[] publicKey = extractByteArray(item.get(KEY_PUBLIC_KEY));
|
||||
|
||||
if (item.containsKey(KEY_SIGNATURE)) {
|
||||
// All PQ prekeys are signed, and therefore have this attribute. Signed EC prekeys are stored
|
||||
// in the Accounts table, so EC prekeys retrieved by this class are never SignedPreKeys.
|
||||
final String signature = Base64.getEncoder().encodeToString(extractByteArray(item.get(KEY_SIGNATURE)));
|
||||
return new SignedPreKey(keyId, publicKey, signature);
|
||||
return new SignedPreKey(keyId, publicKey, extractByteArray(item.get(KEY_SIGNATURE)));
|
||||
}
|
||||
return new PreKey(keyId, publicKey);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user