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

View file

@ -1,6 +1,6 @@
package org.thoughtcrime.securesms.registration.secondary package org.thoughtcrime.securesms.registration.secondary
import com.google.protobuf.ByteString import okio.ByteString
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.instanceOf import org.hamcrest.Matchers.instanceOf
import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.`is`
@ -8,9 +8,9 @@ import org.junit.Test
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.whispersystems.signalservice.internal.crypto.PrimaryProvisioningCipher 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.ProvisioningProtos.ProvisionMessage import org.whispersystems.signalservice.internal.push.ProvisionMessage
import org.whispersystems.signalservice.internal.push.ProvisioningProtos.ProvisioningVersion import org.whispersystems.signalservice.internal.push.ProvisioningVersion
import java.util.UUID import java.util.UUID
class SecondaryProvisioningCipherTest { class SecondaryProvisioningCipherTest {
@ -23,16 +23,18 @@ class SecondaryProvisioningCipherTest {
val primaryProfileKey = ProfileKeyUtil.createNew() val primaryProfileKey = ProfileKeyUtil.createNew()
val primaryProvisioningCipher = PrimaryProvisioningCipher(provisioningCipher.secondaryDevicePublicKey.publicKey) val primaryProvisioningCipher = PrimaryProvisioningCipher(provisioningCipher.secondaryDevicePublicKey.publicKey)
val message = ProvisionMessage.newBuilder() val message = ProvisionMessage(
.setAciIdentityKeyPublic(ByteString.copyFrom(primaryIdentityKeyPair.publicKey.serialize())) aciIdentityKeyPublic = ByteString.of(*primaryIdentityKeyPair.publicKey.serialize()),
.setAciIdentityKeyPrivate(ByteString.copyFrom(primaryIdentityKeyPair.privateKey.serialize())) aciIdentityKeyPrivate = ByteString.of(*primaryIdentityKeyPair.privateKey.serialize()),
.setProvisioningCode("code") provisioningCode = "code",
.setProvisioningVersion(ProvisioningVersion.CURRENT_VALUE) provisioningVersion = ProvisioningVersion.CURRENT.value,
.setNumber("+14045555555") number = "+14045555555",
.setAci(UUID.randomUUID().toString()) aci = UUID.randomUUID().toString(),
.setProfileKey(ByteString.copyFrom(primaryProfileKey.serialize())) 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) val result = provisioningCipher.decrypt(provisionMessage)
assertThat(result, instanceOf(SecondaryProvisioningCipher.ProvisionDecryptResult.Success::class.java)) 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.CdsiAuthResponse;
import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts; import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts;
import org.whispersystems.signalservice.internal.push.ProfileAvatarData; 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.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse; import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
import org.whispersystems.signalservice.internal.push.RemoteConfigResponse; import org.whispersystems.signalservice.internal.push.RemoteConfigResponse;
@ -104,9 +106,6 @@ import javax.annotation.Nullable;
import io.reactivex.rxjava3.core.Single; 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 * The main interface for creating, registering, and
* managing a Signal Service account. * managing a Signal Service account.
@ -662,17 +661,17 @@ public class SignalServiceAccountManager {
Preconditions.checkArgument(pni != null, "Missing PNI!"); Preconditions.checkArgument(pni != null, "Missing PNI!");
PrimaryProvisioningCipher cipher = new PrimaryProvisioningCipher(deviceKey); PrimaryProvisioningCipher cipher = new PrimaryProvisioningCipher(deviceKey);
ProvisionMessage.Builder message = ProvisionMessage.newBuilder() ProvisionMessage.Builder message = new ProvisionMessage.Builder()
.setAciIdentityKeyPublic(ByteString.copyFrom(aciIdentityKeyPair.getPublicKey().serialize())) .aciIdentityKeyPublic(okio.ByteString.of(aciIdentityKeyPair.getPublicKey().serialize()))
.setAciIdentityKeyPrivate(ByteString.copyFrom(aciIdentityKeyPair.getPrivateKey().serialize())) .aciIdentityKeyPrivate(okio.ByteString.of(aciIdentityKeyPair.getPrivateKey().serialize()))
.setPniIdentityKeyPublic(ByteString.copyFrom(pniIdentityKeyPair.getPublicKey().serialize())) .pniIdentityKeyPublic(okio.ByteString.of(pniIdentityKeyPair.getPublicKey().serialize()))
.setPniIdentityKeyPrivate(ByteString.copyFrom(pniIdentityKeyPair.getPrivateKey().serialize())) .pniIdentityKeyPrivate(okio.ByteString.of(pniIdentityKeyPair.getPrivateKey().serialize()))
.setAci(aci.toString()) .aci(aci.toString())
.setPni(pni.toStringWithoutPrefix()) .pni(pni.toStringWithoutPrefix())
.setNumber(e164) .number(e164)
.setProfileKey(ByteString.copyFrom(profileKey.serialize())) .profileKey(okio.ByteString.of(profileKey.serialize()))
.setProvisioningCode(code) .provisioningCode(code)
.setProvisioningVersion(ProvisioningVersion.CURRENT_VALUE); .provisioningVersion(ProvisioningVersion.CURRENT.getValue());
byte[] ciphertext = cipher.encrypt(message.build()); byte[] ciphertext = cipher.encrypt(message.build());
this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext); 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.IdentityCheckRequest;
import org.whispersystems.signalservice.internal.push.IdentityCheckResponse; import org.whispersystems.signalservice.internal.push.IdentityCheckResponse;
import org.whispersystems.signalservice.internal.push.PushServiceSocket; 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.Util;
import org.whispersystems.signalservice.internal.util.concurrent.FutureTransformers; import org.whispersystems.signalservice.internal.util.concurrent.FutureTransformers;
import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture; import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture;
@ -185,15 +185,15 @@ public class SignalServiceMessageReceiver {
InputStream cipherStream = AttachmentCipherInputStream.createForStickerData(manifestBytes, packKey); InputStream cipherStream = AttachmentCipherInputStream.createForStickerData(manifestBytes, packKey);
StickerProtos.Pack pack = StickerProtos.Pack.parseFrom(Util.readFullyAsBytes(cipherStream)); Pack pack = Pack.ADAPTER.decode(Util.readFullyAsBytes(cipherStream));
List<SignalServiceStickerManifest.StickerInfo> stickers = new ArrayList<>(pack.getStickersCount()); List<SignalServiceStickerManifest.StickerInfo> stickers = new ArrayList<>(pack.stickers.size());
SignalServiceStickerManifest.StickerInfo cover = pack.hasCover() ? new SignalServiceStickerManifest.StickerInfo(pack.getCover().getId(), pack.getCover().getEmoji(), pack.getCover().getContentType()) SignalServiceStickerManifest.StickerInfo cover = pack.cover != null ? new SignalServiceStickerManifest.StickerInfo(pack.cover.id, pack.cover.emoji, pack.cover.contentType)
: null; : null;
for (StickerProtos.Pack.Sticker sticker : pack.getStickersList()) { for (Pack.Sticker sticker : pack.stickers) {
stickers.add(new SignalServiceStickerManifest.StickerInfo(sticker.getId(), sticker.getEmoji(), sticker.getContentType())); 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.MismatchedDevices;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage; import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessageList; 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.PushAttachmentData;
import org.whispersystems.signalservice.internal.push.PushServiceSocket; import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.SendGroupMessageResponse; import org.whispersystems.signalservice.internal.push.SendGroupMessageResponse;
@ -1522,7 +1522,7 @@ public class SignalServiceMessageSender {
configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get()); configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get());
} }
configurationMessage.setProvisioningVersion(ProvisioningProtos.ProvisioningVersion.CURRENT_VALUE); configurationMessage.setProvisioningVersion(ProvisioningVersion.CURRENT.getValue());
return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build(); return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build();
} }

View file

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

View file

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