Add contact and key sync message receive support.

This commit is contained in:
Cody Henthorne
2022-01-18 11:10:23 -05:00
committed by Greyson Parrelli
parent c5028720e3
commit c548816daa
17 changed files with 542 additions and 143 deletions

View File

@@ -55,7 +55,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage;
import org.whispersystems.signalservice.api.messages.multidevice.OutgoingPaymentMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
@@ -75,6 +74,7 @@ import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedExcept
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.services.AttachmentService;
import org.whispersystems.signalservice.api.services.MessagingService;
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.Uint64RangeException;
import org.whispersystems.signalservice.api.util.Uint64Util;
@@ -506,7 +506,7 @@ public class SignalServiceMessageSender {
} else if (message.getConfiguration().isPresent()) {
content = createMultiDeviceConfigurationContent(message.getConfiguration().get());
} else if (message.getSent().isPresent()) {
content = createMultiDeviceSentTranscriptContent(message.getSent().get(), unidentifiedAccess);
content = createMultiDeviceSentTranscriptContent(message.getSent().get(), unidentifiedAccess.isPresent());
} else if (message.getStickerPackOperations().isPresent()) {
content = createMultiDeviceStickerPackOperationContent(message.getStickerPackOperations().get());
} else if (message.getFetchType().isPresent()) {
@@ -518,7 +518,7 @@ public class SignalServiceMessageSender {
} else if (message.getKeys().isPresent()) {
content = createMultiDeviceSyncKeysContent(message.getKeys().get());
} else if (message.getVerified().isPresent()) {
return sendVerifiedMessage(message.getVerified().get(), unidentifiedAccess);
return sendVerifiedSyncMessage(message.getVerified().get());
} else if (message.getRequest().isPresent()) {
content = createRequestContent(message.getRequest().get().getRequest());
} else {
@@ -640,7 +640,7 @@ public class SignalServiceMessageSender {
attachment.getUploadTimestamp());
}
private SendMessageResult sendVerifiedMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
private SendMessageResult sendVerifiedSyncMessage(VerifiedMessage message)
throws IOException, UntrustedIdentityException
{
byte[] nullMessageBody = DataMessage.newBuilder()
@@ -658,7 +658,7 @@ public class SignalServiceMessageSender {
EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.absent());
SendMessageResult result = sendMessage(message.getDestination(), getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), envelopeContent, false, null);
SendMessageResult result = sendMessage(message.getDestination(), Optional.absent(), message.getTimestamp(), envelopeContent, false, null);
if (result.getSuccess().isNeedsSync()) {
Content syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray());
@@ -1026,10 +1026,10 @@ public class SignalServiceMessageSender {
return container.setSyncMessage(builder).build();
}
private Content createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional<UnidentifiedAccessPair> unidentifiedAccess) throws IOException {
private Content createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, boolean unidentifiedAccess) throws IOException {
SignalServiceAddress address = transcript.getDestination().get();
Content content = createMessageContent(transcript.getMessage());
SendMessageResult result = SendMessageResult.success(address, Collections.emptyList(), unidentifiedAccess.isPresent(), true, -1, Optional.of(content));
SendMessageResult result = SendMessageResult.success(address, Collections.emptyList(), unidentifiedAccess, true, -1, Optional.of(content));
return createMultiDeviceSentTranscriptContent(content,
Optional.of(address),
@@ -1910,63 +1910,7 @@ public class SignalServiceMessageSender {
}
private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) {
AttachmentPointer.Builder builder = AttachmentPointer.newBuilder()
.setCdnNumber(attachment.getCdnNumber())
.setContentType(attachment.getContentType())
.setKey(ByteString.copyFrom(attachment.getKey()))
.setDigest(ByteString.copyFrom(attachment.getDigest().get()))
.setSize(attachment.getSize().get())
.setUploadTimestamp(attachment.getUploadTimestamp());
if (attachment.getRemoteId().getV2().isPresent()) {
builder.setCdnId(attachment.getRemoteId().getV2().get());
}
if (attachment.getRemoteId().getV3().isPresent()) {
builder.setCdnKey(attachment.getRemoteId().getV3().get());
}
if (attachment.getFileName().isPresent()) {
builder.setFileName(attachment.getFileName().get());
}
if (attachment.getPreview().isPresent()) {
builder.setThumbnail(ByteString.copyFrom(attachment.getPreview().get()));
}
if (attachment.getWidth() > 0) {
builder.setWidth(attachment.getWidth());
}
if (attachment.getHeight() > 0) {
builder.setHeight(attachment.getHeight());
}
int flags = 0;
if (attachment.getVoiceNote()) {
flags |= FlagUtil.toBinaryFlag(AttachmentPointer.Flags.VOICE_MESSAGE_VALUE);
}
if (attachment.isBorderless()) {
flags |= FlagUtil.toBinaryFlag(AttachmentPointer.Flags.BORDERLESS_VALUE);
}
if (attachment.isGif()) {
flags |= FlagUtil.toBinaryFlag(AttachmentPointer.Flags.GIF_VALUE);
}
builder.setFlags(flags);
if (attachment.getCaption().isPresent()) {
builder.setCaption(attachment.getCaption().get());
}
if (attachment.getBlurHash().isPresent()) {
builder.setBlurHash(attachment.getBlurHash().get());
}
return builder.build();
return AttachmentPointerUtil.createAttachmentPointer(attachment);
}
private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment)
@@ -1986,12 +1930,17 @@ public class SignalServiceMessageSender {
{
List<OutgoingPushMessage> messages = new LinkedList<>();
boolean isLocalPrimaryDevice = recipient.matches(localAddress) && localDeviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
if (!isLocalPrimaryDevice || unidentifiedAccess.isPresent()) {
messages.add(getEncryptedMessage(socket, recipient, unidentifiedAccess, SignalServiceAddress.DEFAULT_DEVICE_ID, plaintext));
List<Integer> subDevices = store.getSubDeviceSessions(recipient.getIdentifier());
List<Integer> deviceIds = new ArrayList<>(subDevices.size() + 1);
deviceIds.add(SignalServiceAddress.DEFAULT_DEVICE_ID);
deviceIds.addAll(subDevices);
if (recipient.matches(localAddress)) {
deviceIds.remove(Integer.valueOf(localDeviceId));
}
for (int deviceId : store.getSubDeviceSessions(recipient.getIdentifier())) {
for (int deviceId : deviceIds) {
if (store.containsSession(new SignalProtocolAddress(recipient.getIdentifier(), deviceId))) {
messages.add(getEncryptedMessage(socket, recipient, unidentifiedAccess, deviceId, plaintext));
}
@@ -2010,8 +1959,7 @@ public class SignalServiceMessageSender {
SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getIdentifier(), deviceId);
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, localDeviceId, store, sessionLock, null);
boolean isLocalDevice = recipient.matches(localAddress) && deviceId == localDeviceId;
if (!store.containsSession(signalProtocolAddress) && !isLocalDevice) {
if (!store.containsSession(signalProtocolAddress)) {
try {
List<PreKeyBundle> preKeys = socket.getPreKeys(recipient, unidentifiedAccess, deviceId);

View File

@@ -130,6 +130,17 @@ public class AttachmentCipherInputStream extends FilterInputStream {
}
}
@Override
public int read() throws IOException {
byte[] buffer = new byte[1];
int read;
//noinspection StatementWithEmptyBody
while ((read = read(buffer)) == 0);
return (read == -1) ? -1 : ((int) buffer[0]) & 0xFF;
}
@Override
public int read(byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);

View File

@@ -31,6 +31,8 @@ import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage;
import org.whispersystems.signalservice.api.messages.multidevice.OutgoingPaymentMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
@@ -45,6 +47,8 @@ import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.payments.Money;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.StorageKey;
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
@@ -813,6 +817,16 @@ public final class SignalServiceContent {
}
}
if (content.hasKeys() && content.getKeys().hasStorageService()) {
byte[] storageKey = content.getKeys().getStorageService().toByteArray();
return SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.of(new StorageKey(storageKey))));
}
if (content.hasContacts()) {
return SignalServiceSyncMessage.forContacts(new ContactsMessage(createAttachmentPointer(content.getContacts().getBlob()), content.getContacts().getComplete()));
}
return SignalServiceSyncMessage.empty();
}
@@ -1162,21 +1176,7 @@ public final class SignalServiceContent {
}
private static SignalServiceAttachmentPointer createAttachmentPointer(SignalServiceProtos.AttachmentPointer pointer) throws InvalidMessageStructureException {
return new SignalServiceAttachmentPointer(pointer.getCdnNumber(),
SignalServiceAttachmentRemoteId.from(pointer),
pointer.getContentType(),
pointer.getKey().toByteArray(),
pointer.hasSize() ? Optional.of(pointer.getSize()) : Optional.<Integer>absent(),
pointer.hasThumbnail() ? Optional.of(pointer.getThumbnail().toByteArray()): Optional.<byte[]>absent(),
pointer.getWidth(), pointer.getHeight(),
pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.<byte[]>absent(),
pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.<String>absent(),
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE_VALUE)) != 0,
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.BORDERLESS_VALUE)) != 0,
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.GIF_VALUE)) != 0,
pointer.hasCaption() ? Optional.of(pointer.getCaption()) : Optional.<String>absent(),
pointer.hasBlurHash() ? Optional.of(pointer.getBlurHash()) : Optional.<String>absent(),
pointer.hasUploadTimestamp() ? pointer.getUploadTimestamp() : 0);
return AttachmentPointerUtil.createSignalAttachmentPointer(pointer);
}

View File

@@ -12,63 +12,48 @@ public class ChunkedInputStream {
this.in = in;
}
protected int readRawVarint32() throws IOException {
byte tmp = (byte)in.read();
if (tmp >= 0) {
return tmp;
}
int result = tmp & 0x7f;
if ((tmp = (byte)in.read()) >= 0) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = (byte)in.read()) >= 0) {
result |= tmp << 14;
} else {
result |= (tmp & 0x7f) << 14;
if ((tmp = (byte)in.read()) >= 0) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = (byte)in.read()) << 28;
if (tmp < 0) {
// Discard upper 32 bits.
for (int i = 0; i < 5; i++) {
if ((byte)in.read() >= 0) {
return result;
}
}
throw new IOException("Malformed varint!");
}
}
long readRawVarint32() throws IOException {
long result = 0;
for (int shift = 0; shift < 32; shift += 7) {
int tmpInt = in.read();
if (tmpInt < 0) {
return -1;
}
final byte b = (byte) tmpInt;
result |= (long) (b & 0x7F) << shift;
if ((b & 0x80) == 0) {
return result;
}
}
return result;
throw new IOException("Malformed varint!");
}
protected static final class LimitedInputStream extends FilterInputStream {
protected static final class LimitedInputStream extends InputStream {
private final InputStream in;
private long left;
private long mark = -1;
LimitedInputStream(InputStream in, long limit) {
super(in);
left = limit;
this.in = in;
this.left = limit;
}
@Override public int available() throws IOException {
@Override
public int available() throws IOException {
return (int) Math.min(in.available(), left);
}
// it's okay to mark even if mark isn't supported, as reset won't work
@Override public synchronized void mark(int readLimit) {
@Override
public synchronized void mark(int readLimit) {
in.mark(readLimit);
mark = left;
}
@Override public int read() throws IOException {
@Override
public int read() throws IOException {
if (left == 0) {
return -1;
}
@@ -80,7 +65,8 @@ public class ChunkedInputStream {
return result;
}
@Override public int read(byte[] b, int off, int len) throws IOException {
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (left == 0) {
return -1;
}
@@ -93,7 +79,8 @@ public class ChunkedInputStream {
return result;
}
@Override public synchronized void reset() throws IOException {
@Override
public synchronized void reset() throws IOException {
if (!in.markSupported()) {
throw new IOException("Mark not supported");
}
@@ -105,12 +92,18 @@ public class ChunkedInputStream {
left = mark;
}
@Override public long skip(long n) throws IOException {
@Override
public long skip(long n) throws IOException {
n = Math.min(n, left);
long skipped = in.skip(n);
left -= skipped;
return skipped;
}
@Override
public void close() throws IOException {
// do nothing
}
}
}

View File

@@ -16,7 +16,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.util.Util;
@@ -32,8 +31,12 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
}
public DeviceContact read() throws IOException {
long detailsLength = readRawVarint32();
byte[] detailsSerialized = new byte[(int)detailsLength];
int detailsLength = (int) readRawVarint32();
if (detailsLength == -1) {
return null;
}
byte[] detailsSerialized = new byte[(int) detailsLength];
Util.readFully(in, detailsSerialized);
SignalServiceProtos.ContactDetails details = SignalServiceProtos.ContactDetails.parseFrom(detailsSerialized);

View File

@@ -0,0 +1,96 @@
package org.whispersystems.signalservice.api.util;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.util.FlagUtil;
public final class AttachmentPointerUtil {
public static SignalServiceAttachmentPointer createSignalAttachmentPointer(byte[] pointer) throws InvalidMessageStructureException, InvalidProtocolBufferException {
return createSignalAttachmentPointer(SignalServiceProtos.AttachmentPointer.parseFrom(pointer));
}
public static SignalServiceAttachmentPointer createSignalAttachmentPointer(SignalServiceProtos.AttachmentPointer pointer) throws InvalidMessageStructureException {
return new SignalServiceAttachmentPointer(pointer.getCdnNumber(),
SignalServiceAttachmentRemoteId.from(pointer),
pointer.getContentType(),
pointer.getKey().toByteArray(),
pointer.hasSize() ? Optional.of(pointer.getSize()) : Optional.<Integer>absent(),
pointer.hasThumbnail() ? Optional.of(pointer.getThumbnail().toByteArray()): Optional.<byte[]>absent(),
pointer.getWidth(), pointer.getHeight(),
pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.<byte[]>absent(),
pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.<String>absent(),
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE_VALUE)) != 0,
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.BORDERLESS_VALUE)) != 0,
(pointer.getFlags() & FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.GIF_VALUE)) != 0,
pointer.hasCaption() ? Optional.of(pointer.getCaption()) : Optional.<String>absent(),
pointer.hasBlurHash() ? Optional.of(pointer.getBlurHash()) : Optional.<String>absent(),
pointer.hasUploadTimestamp() ? pointer.getUploadTimestamp() : 0);
}
public static SignalServiceProtos.AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) {
SignalServiceProtos.AttachmentPointer.Builder builder = SignalServiceProtos.AttachmentPointer.newBuilder()
.setCdnNumber(attachment.getCdnNumber())
.setContentType(attachment.getContentType())
.setKey(ByteString.copyFrom(attachment.getKey()))
.setDigest(ByteString.copyFrom(attachment.getDigest().get()))
.setSize(attachment.getSize().get())
.setUploadTimestamp(attachment.getUploadTimestamp());
if (attachment.getRemoteId().getV2().isPresent()) {
builder.setCdnId(attachment.getRemoteId().getV2().get());
}
if (attachment.getRemoteId().getV3().isPresent()) {
builder.setCdnKey(attachment.getRemoteId().getV3().get());
}
if (attachment.getFileName().isPresent()) {
builder.setFileName(attachment.getFileName().get());
}
if (attachment.getPreview().isPresent()) {
builder.setThumbnail(ByteString.copyFrom(attachment.getPreview().get()));
}
if (attachment.getWidth() > 0) {
builder.setWidth(attachment.getWidth());
}
if (attachment.getHeight() > 0) {
builder.setHeight(attachment.getHeight());
}
int flags = 0;
if (attachment.getVoiceNote()) {
flags |= FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE_VALUE);
}
if (attachment.isBorderless()) {
flags |= FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.BORDERLESS_VALUE);
}
if (attachment.isGif()) {
flags |= FlagUtil.toBinaryFlag(SignalServiceProtos.AttachmentPointer.Flags.GIF_VALUE);
}
builder.setFlags(flags);
if (attachment.getCaption().isPresent()) {
builder.setCaption(attachment.getCaption().get());
}
if (attachment.getBlurHash().isPresent()) {
builder.setBlurHash(attachment.getBlurHash().get());
}
return builder.build();
}
}

View File

@@ -0,0 +1,89 @@
package org.whispersystems.signalservice.api.messages.multidevice;
import org.junit.Test;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECKeyPair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class DeviceContactsInputStreamTest {
@Test
public void read() throws IOException, InvalidInputException {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
DeviceContactsOutputStream output = new DeviceContactsOutputStream(byteArrayOut);
SignalServiceAddress addressFirst = new SignalServiceAddress(ACI.from(UUID.randomUUID()), "+1404555555");
SignalServiceAddress addressSecond = new SignalServiceAddress(ACI.from(UUID.randomUUID()), "+1444555555");
DeviceContact first = new DeviceContact(
addressFirst,
Optional.of("Teal'c"),
Optional.absent(),
Optional.of("ultramarine"),
Optional.of(new VerifiedMessage(addressFirst, generateIdentityKey(), VerifiedMessage.VerifiedState.DEFAULT, System.currentTimeMillis())),
Optional.of(generateProfileKey()),
false,
Optional.of(0),
Optional.of(0),
false
);
DeviceContact second = new DeviceContact(
addressSecond,
Optional.of("Bra'tac"),
Optional.absent(),
Optional.of("ultramarine"),
Optional.of(new VerifiedMessage(addressSecond, generateIdentityKey(), VerifiedMessage.VerifiedState.DEFAULT, System.currentTimeMillis())),
Optional.of(generateProfileKey()),
false,
Optional.of(0),
Optional.of(0),
false
);
output.write(first);
output.write(second);
output.close();
ByteArrayInputStream byteArrayIn = new ByteArrayInputStream(byteArrayOut.toByteArray());
DeviceContactsInputStream input = new DeviceContactsInputStream(byteArrayIn);
DeviceContact readFirst = input.read();
DeviceContact readSecond = input.read();
assertEquals(first.getAddress(), readFirst.getAddress());
assertEquals(first.getName(), readFirst.getName());
assertEquals(first.getColor(), readFirst.getColor());
assertEquals(first.getVerified().get().getIdentityKey(), readFirst.getVerified().get().getIdentityKey());
assertEquals(first.isArchived(), readFirst.isArchived());
assertEquals(second.getAddress(), readSecond.getAddress());
assertEquals(second.getName(), readSecond.getName());
assertEquals(second.getColor(), readSecond.getColor());
assertEquals(second.getVerified().get().getIdentityKey(), readSecond.getVerified().get().getIdentityKey());
assertEquals(second.isArchived(), readSecond.isArchived());
}
private static IdentityKey generateIdentityKey() {
ECKeyPair djbKeyPair = Curve.generateKeyPair();
return new IdentityKey(djbKeyPair.getPublicKey());
}
private static ProfileKey generateProfileKey() throws InvalidInputException {
return new ProfileKey(Util.getSecretBytes(32));
}
}