Convert Provisioning, ResumeableUploads, and StickerResources protos to wire.

This commit is contained in:
Cody Henthorne 2023-08-23 23:07:58 -04:00
parent 611f074a9d
commit fba9b46fe9
10 changed files with 78 additions and 78 deletions

View file

@ -9,7 +9,8 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher
import org.whispersystems.signalservice.internal.push.ProvisioningProtos
import org.whispersystems.signalservice.internal.push.ProvisionEnvelope
import org.whispersystems.signalservice.internal.push.ProvisionMessage
import java.security.InvalidKeyException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
@ -26,9 +27,9 @@ class SecondaryProvisioningCipher private constructor(private val secondaryIdent
val secondaryDevicePublicKey: IdentityKey = secondaryIdentityKeyPair.publicKey
fun decrypt(envelope: ProvisioningProtos.ProvisionEnvelope): ProvisionDecryptResult {
val primaryEphemeralPublicKey = envelope.publicKey.toByteArray()
val body = envelope.body.toByteArray()
fun decrypt(envelope: ProvisionEnvelope): ProvisionDecryptResult {
val primaryEphemeralPublicKey = envelope.publicKey!!.toByteArray()
val body = envelope.body!!.toByteArray()
val provisionMessageLength = body.size - VERSION_LENGTH - IV_LENGTH - MAC_LENGTH
@ -64,17 +65,17 @@ class SecondaryProvisioningCipher private constructor(private val secondaryIdent
return ProvisionDecryptResult.Error
}
val provisioningMessage = ProvisioningProtos.ProvisionMessage.parseFrom(plaintext)
val provisioningMessage = ProvisionMessage.ADAPTER.decode(plaintext)
return ProvisionDecryptResult.Success(
uuid = UuidUtil.parseOrThrow(provisioningMessage.aci),
e164 = provisioningMessage.number,
identityKeyPair = IdentityKeyPair(IdentityKey(provisioningMessage.aciIdentityKeyPublic.toByteArray()), Curve.decodePrivatePoint(provisioningMessage.aciIdentityKeyPrivate.toByteArray())),
profileKey = ProfileKey(provisioningMessage.profileKey.toByteArray()),
areReadReceiptsEnabled = provisioningMessage.readReceipts,
e164 = provisioningMessage.number!!,
identityKeyPair = IdentityKeyPair(IdentityKey(provisioningMessage.aciIdentityKeyPublic!!.toByteArray()), Curve.decodePrivatePoint(provisioningMessage.aciIdentityKeyPrivate!!.toByteArray())),
profileKey = ProfileKey(provisioningMessage.profileKey!!.toByteArray()),
areReadReceiptsEnabled = provisioningMessage.readReceipts == true,
primaryUserAgent = provisioningMessage.userAgent,
provisioningCode = provisioningMessage.provisioningCode,
provisioningVersion = provisioningMessage.provisioningVersion
provisioningCode = provisioningMessage.provisioningCode!!,
provisioningVersion = provisioningMessage.provisioningVersion!!
)
}

View file

@ -1,6 +1,6 @@
package org.thoughtcrime.securesms.registration.secondary
import com.google.protobuf.ByteString
import okio.ByteString
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.instanceOf
import org.hamcrest.Matchers.`is`
@ -8,9 +8,9 @@ import org.junit.Test
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher
import org.whispersystems.signalservice.internal.push.ProvisioningProtos
import org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage
import org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisioningVersion
import org.whispersystems.signalservice.internal.push.ProvisionEnvelope
import org.whispersystems.signalservice.internal.push.ProvisionMessage
import org.whispersystems.signalservice.internal.push.ProvisioningVersion
import java.util.UUID
class SecondaryProvisioningCipherTest {
@ -23,16 +23,18 @@ class SecondaryProvisioningCipherTest {
val primaryProfileKey = ProfileKeyUtil.createNew()
val primaryProvisioningCipher = PrimaryProvisioningCipher(provisioningCipher.secondaryDevicePublicKey.publicKey)
val message = ProvisionMessage.newBuilder()
.setAciIdentityKeyPublic(ByteString.copyFrom(primaryIdentityKeyPair.publicKey.serialize()))
.setAciIdentityKeyPrivate(ByteString.copyFrom(primaryIdentityKeyPair.privateKey.serialize()))
.setProvisioningCode("code")
.setProvisioningVersion(ProvisioningVersion.CURRENT_VALUE)
.setNumber("+14045555555")
.setAci(UUID.randomUUID().toString())
.setProfileKey(ByteString.copyFrom(primaryProfileKey.serialize()))
val message = ProvisionMessage(
aciIdentityKeyPublic = ByteString.of(*primaryIdentityKeyPair.publicKey.serialize()),
aciIdentityKeyPrivate = ByteString.of(*primaryIdentityKeyPair.privateKey.serialize()),
provisioningCode = "code",
provisioningVersion = ProvisioningVersion.CURRENT.value,
number = "+14045555555",
aci = UUID.randomUUID().toString(),
profileKey = ByteString.of(*primaryProfileKey.serialize()),
readReceipts = true
)
val provisionMessage = ProvisioningProtos.ProvisionEnvelope.parseFrom(primaryProvisioningCipher.encrypt(message.build()))
val provisionMessage = ProvisionEnvelope.ADAPTER.decode(primaryProvisioningCipher.encrypt(message))
val result = provisioningCipher.decrypt(provisionMessage)
assertThat(result, instanceOf(SecondaryProvisioningCipher.ProvisionDecryptResult.Success::class.java))

View file

@ -61,6 +61,8 @@ import org.whispersystems.signalservice.internal.push.BackupAuthCheckResponse;
import org.whispersystems.signalservice.internal.push.CdsiAuthResponse;
import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts;
import org.whispersystems.signalservice.internal.push.ProfileAvatarData;
import org.whispersystems.signalservice.internal.push.ProvisionMessage;
import org.whispersystems.signalservice.internal.push.ProvisioningVersion;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
import org.whispersystems.signalservice.internal.push.RemoteConfigResponse;
@ -104,9 +106,6 @@ import javax.annotation.Nullable;
import io.reactivex.rxjava3.core.Single;
import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage;
import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisioningVersion;
/**
* The main interface for creating, registering, and
* managing a Signal Service account.
@ -662,17 +661,17 @@ public class SignalServiceAccountManager {
Preconditions.checkArgument(pni != null, "Missing PNI!");
PrimaryProvisioningCipher cipher = new PrimaryProvisioningCipher(deviceKey);
ProvisionMessage.Builder message = ProvisionMessage.newBuilder()
.setAciIdentityKeyPublic(ByteString.copyFrom(aciIdentityKeyPair.getPublicKey().serialize()))
.setAciIdentityKeyPrivate(ByteString.copyFrom(aciIdentityKeyPair.getPrivateKey().serialize()))
.setPniIdentityKeyPublic(ByteString.copyFrom(pniIdentityKeyPair.getPublicKey().serialize()))
.setPniIdentityKeyPrivate(ByteString.copyFrom(pniIdentityKeyPair.getPrivateKey().serialize()))
.setAci(aci.toString())
.setPni(pni.toStringWithoutPrefix())
.setNumber(e164)
.setProfileKey(ByteString.copyFrom(profileKey.serialize()))
.setProvisioningCode(code)
.setProvisioningVersion(ProvisioningVersion.CURRENT_VALUE);
ProvisionMessage.Builder message = new ProvisionMessage.Builder()
.aciIdentityKeyPublic(okio.ByteString.of(aciIdentityKeyPair.getPublicKey().serialize()))
.aciIdentityKeyPrivate(okio.ByteString.of(aciIdentityKeyPair.getPrivateKey().serialize()))
.pniIdentityKeyPublic(okio.ByteString.of(pniIdentityKeyPair.getPublicKey().serialize()))
.pniIdentityKeyPrivate(okio.ByteString.of(pniIdentityKeyPair.getPrivateKey().serialize()))
.aci(aci.toString())
.pni(pni.toStringWithoutPrefix())
.number(e164)
.profileKey(okio.ByteString.of(profileKey.serialize()))
.provisioningCode(code)
.provisioningVersion(ProvisioningVersion.CURRENT.getValue());
byte[] ciphertext = cipher.encrypt(message.build());
this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext);

View file

@ -27,7 +27,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf
import org.whispersystems.signalservice.internal.push.IdentityCheckRequest;
import org.whispersystems.signalservice.internal.push.IdentityCheckResponse;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.sticker.StickerProtos;
import org.whispersystems.signalservice.internal.sticker.Pack;
import org.whispersystems.signalservice.internal.util.Util;
import org.whispersystems.signalservice.internal.util.concurrent.FutureTransformers;
import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture;
@ -185,15 +185,15 @@ public class SignalServiceMessageReceiver {
InputStream cipherStream = AttachmentCipherInputStream.createForStickerData(manifestBytes, packKey);
StickerProtos.Pack pack = StickerProtos.Pack.parseFrom(Util.readFullyAsBytes(cipherStream));
List<SignalServiceStickerManifest.StickerInfo> stickers = new ArrayList<>(pack.getStickersCount());
SignalServiceStickerManifest.StickerInfo cover = pack.hasCover() ? new SignalServiceStickerManifest.StickerInfo(pack.getCover().getId(), pack.getCover().getEmoji(), pack.getCover().getContentType())
: null;
Pack pack = Pack.ADAPTER.decode(Util.readFullyAsBytes(cipherStream));
List<SignalServiceStickerManifest.StickerInfo> stickers = new ArrayList<>(pack.stickers.size());
SignalServiceStickerManifest.StickerInfo cover = pack.cover != null ? new SignalServiceStickerManifest.StickerInfo(pack.cover.id, pack.cover.emoji, pack.cover.contentType)
: null;
for (StickerProtos.Pack.Sticker sticker : pack.getStickersList()) {
stickers.add(new SignalServiceStickerManifest.StickerInfo(sticker.getId(), sticker.getEmoji(), sticker.getContentType()));
for (Pack.Sticker sticker : pack.stickers) {
stickers.add(new SignalServiceStickerManifest.StickerInfo(sticker.id, sticker.emoji, sticker.contentType));
}
return new SignalServiceStickerManifest(pack.getTitle(), pack.getAuthor(), cover, stickers);
return new SignalServiceStickerManifest(pack.title, pack.author, cover, stickers);
}
}

View file

@ -96,7 +96,7 @@ import org.whispersystems.signalservice.internal.push.GroupStaleDevices;
import org.whispersystems.signalservice.internal.push.MismatchedDevices;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessageList;
import org.whispersystems.signalservice.internal.push.ProvisioningProtos;
import org.whispersystems.signalservice.internal.push.ProvisioningVersion;
import org.whispersystems.signalservice.internal.push.PushAttachmentData;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.SendGroupMessageResponse;
@ -1522,7 +1522,7 @@ public class SignalServiceMessageSender {
configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get());
}
configurationMessage.setProvisioningVersion(ProvisioningProtos.ProvisioningVersion.CURRENT_VALUE);
configurationMessage.setProvisioningVersion(ProvisioningVersion.CURRENT.getValue());
return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build();
}

View file

@ -6,13 +6,13 @@
package org.whispersystems.signalservice.internal.crypto;
import com.google.protobuf.ByteString;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.protocol.kdf.HKDF;
import org.whispersystems.signalservice.internal.push.ProvisionEnvelope;
import org.whispersystems.signalservice.internal.push.ProvisionMessage;
import org.whispersystems.signalservice.internal.util.Util;
import java.security.NoSuchAlgorithmException;
@ -24,9 +24,7 @@ import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionEnvelope;
import static org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisionMessage;
import okio.ByteString;
public class PrimaryProvisioningCipher {
@ -45,15 +43,15 @@ public class PrimaryProvisioningCipher {
byte[][] parts = Util.split(derivedSecret, 32, 32);
byte[] version = {0x01};
byte[] ciphertext = getCiphertext(parts[0], message.toByteArray());
byte[] ciphertext = getCiphertext(parts[0], message.encode());
byte[] mac = getMac(parts[1], Util.join(version, ciphertext));
byte[] body = Util.join(version, ciphertext, mac);
return ProvisionEnvelope.newBuilder()
.setPublicKey(ByteString.copyFrom(ourKeyPair.getPublicKey().serialize()))
.setBody(ByteString.copyFrom(body))
.build()
.toByteArray();
return new ProvisionEnvelope.Builder()
.publicKey(ByteString.of(ourKeyPair.getPublicKey().serialize()))
.body(ByteString.of(body))
.build()
.encode();
}
private byte[] getCiphertext(byte[] key, byte[] message) {

View file

@ -1,13 +1,13 @@
package org.whispersystems.signalservice.internal.push.http;
import com.google.protobuf.ByteString;
import org.signal.protos.resumableuploads.ResumableUploads;
import org.signal.protos.resumableuploads.ResumableUpload;
import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException;
import org.whispersystems.util.Base64;
import java.io.IOException;
import okio.ByteString;
public final class ResumableUploadSpec {
private final byte[] secretKey;
@ -58,31 +58,31 @@ public final class ResumableUploadSpec {
}
public String serialize() {
ResumableUploads.ResumableUpload.Builder builder = ResumableUploads.ResumableUpload.newBuilder()
.setSecretKey(ByteString.copyFrom(getSecretKey()))
.setIv(ByteString.copyFrom(getIV()))
.setTimeout(getExpirationTimestamp())
.setCdnNumber(getCdnNumber())
.setCdnKey(getCdnKey())
.setLocation(getResumeLocation())
.setTimeout(getExpirationTimestamp());
ResumableUpload.Builder builder = new ResumableUpload.Builder()
.secretKey(ByteString.of(getSecretKey()))
.iv(ByteString.of(getIV()))
.timeout(getExpirationTimestamp())
.cdnNumber(getCdnNumber())
.cdnKey(getCdnKey())
.location(getResumeLocation())
.timeout(getExpirationTimestamp());
return Base64.encodeBytes(builder.build().toByteArray());
return Base64.encodeBytes(builder.build().encode());
}
public static ResumableUploadSpec deserialize(String serializedSpec) throws ResumeLocationInvalidException {
if (serializedSpec == null) return null;
try {
ResumableUploads.ResumableUpload resumableUpload = ResumableUploads.ResumableUpload.parseFrom(ByteString.copyFrom(Base64.decode(serializedSpec)));
ResumableUpload resumableUpload = ResumableUpload.ADAPTER.decode(Base64.decode(serializedSpec));
return new ResumableUploadSpec(
resumableUpload.getSecretKey().toByteArray(),
resumableUpload.getIv().toByteArray(),
resumableUpload.getCdnKey(),
resumableUpload.getCdnNumber(),
resumableUpload.getLocation(),
resumableUpload.getTimeout()
resumableUpload.secretKey.toByteArray(),
resumableUpload.iv.toByteArray(),
resumableUpload.cdnKey,
resumableUpload.cdnNumber,
resumableUpload.location,
resumableUpload.timeout
);
} catch (IOException e) {
throw new ResumeLocationInvalidException();