mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-21 11:38:36 +00:00
Avoid some 401 errors during story sends.
This commit is contained in:
@@ -96,7 +96,6 @@ public class UnidentifiedAccessUtil {
|
|||||||
Map<CertificateType, Integer> typeCounts = new HashMap<>();
|
Map<CertificateType, Integer> typeCounts = new HashMap<>();
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients) {
|
||||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient, isForStory);
|
|
||||||
CertificateType certificateType = getUnidentifiedAccessCertificateType(recipient);
|
CertificateType certificateType = getUnidentifiedAccessCertificateType(recipient);
|
||||||
byte[] ourUnidentifiedAccessCertificate = SignalStore.certificateValues().getUnidentifiedAccessCertificate(certificateType);
|
byte[] ourUnidentifiedAccessCertificate = SignalStore.certificateValues().getUnidentifiedAccessCertificate(certificateType);
|
||||||
|
|
||||||
@@ -104,17 +103,22 @@ public class UnidentifiedAccessUtil {
|
|||||||
typeCount++;
|
typeCount++;
|
||||||
typeCounts.put(certificateType, typeCount);
|
typeCounts.put(certificateType, typeCount);
|
||||||
|
|
||||||
if (theirUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
|
if (ourUnidentifiedAccessCertificate != null) {
|
||||||
try {
|
try {
|
||||||
access.add(Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey,
|
UnidentifiedAccess theirAccess = getTargetUnidentifiedAccess(recipient, ourUnidentifiedAccessCertificate, isForStory);
|
||||||
ourUnidentifiedAccessCertificate),
|
UnidentifiedAccess ourAccess = new UnidentifiedAccess(ourUnidentifiedAccessKey, ourUnidentifiedAccessCertificate, false);
|
||||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
|
||||||
ourUnidentifiedAccessCertificate))));
|
if (theirAccess != null) {
|
||||||
|
access.add(Optional.of(new UnidentifiedAccessPair(theirAccess, ourAccess)));
|
||||||
|
} else {
|
||||||
|
access.add(Optional.empty());
|
||||||
|
}
|
||||||
} catch (InvalidCertificateException e) {
|
} catch (InvalidCertificateException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, "Invalid unidentified access certificate!", e);
|
||||||
access.add(Optional.empty());
|
access.add(Optional.empty());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Log.w(TAG, "Missing unidentified access certificate!");
|
||||||
access.add(Optional.empty());
|
access.add(Optional.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,9 +144,11 @@ public class UnidentifiedAccessUtil {
|
|||||||
|
|
||||||
if (ourUnidentifiedAccessCertificate != null) {
|
if (ourUnidentifiedAccessCertificate != null) {
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
||||||
ourUnidentifiedAccessCertificate),
|
ourUnidentifiedAccessCertificate,
|
||||||
|
false),
|
||||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
||||||
ourUnidentifiedAccessCertificate)));
|
ourUnidentifiedAccessCertificate,
|
||||||
|
false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -168,7 +174,7 @@ public class UnidentifiedAccessUtil {
|
|||||||
.getUnidentifiedAccessCertificate(getUnidentifiedAccessCertificateType(recipient));
|
.getUnidentifiedAccessCertificate(getUnidentifiedAccessCertificateType(recipient));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient, boolean isForStory) {
|
private static @Nullable UnidentifiedAccess getTargetUnidentifiedAccess(@NonNull Recipient recipient, @NonNull byte[] certificate, boolean isForStory) throws InvalidCertificateException {
|
||||||
ProfileKey theirProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey());
|
ProfileKey theirProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey());
|
||||||
|
|
||||||
byte[] accessKey;
|
byte[] accessKey;
|
||||||
@@ -176,7 +182,11 @@ public class UnidentifiedAccessUtil {
|
|||||||
switch (recipient.resolve().getUnidentifiedAccessMode()) {
|
switch (recipient.resolve().getUnidentifiedAccessMode()) {
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
if (theirProfileKey == null) {
|
if (theirProfileKey == null) {
|
||||||
|
if (isForStory) {
|
||||||
|
accessKey = null;
|
||||||
|
} else {
|
||||||
accessKey = UNRESTRICTED_KEY;
|
accessKey = UNRESTRICTED_KEY;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||||
}
|
}
|
||||||
@@ -199,10 +209,12 @@ public class UnidentifiedAccessUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (accessKey == null && isForStory) {
|
if (accessKey == null && isForStory) {
|
||||||
accessKey = UNRESTRICTED_KEY;
|
return new UnidentifiedAccess(UNRESTRICTED_KEY, certificate, true);
|
||||||
|
} else if (accessKey != null) {
|
||||||
|
return new UnidentifiedAccess(accessKey, certificate, false);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CertificateValidatorHolder {
|
private enum CertificateValidatorHolder {
|
||||||
|
|||||||
@@ -2378,9 +2378,15 @@ public class SignalServiceMessageSender {
|
|||||||
|
|
||||||
private List<PreKeyBundle> getPreKeys(SignalServiceAddress recipient, Optional<UnidentifiedAccess> unidentifiedAccess, int deviceId, boolean story) throws IOException {
|
private List<PreKeyBundle> getPreKeys(SignalServiceAddress recipient, Optional<UnidentifiedAccess> unidentifiedAccess, int deviceId, boolean story) throws IOException {
|
||||||
try {
|
try {
|
||||||
|
// If it's only unrestricted because it's a story send, then we know it'll fail
|
||||||
|
if (story && unidentifiedAccess.isPresent() && unidentifiedAccess.get().isUnrestrictedForStory()) {
|
||||||
|
unidentifiedAccess = Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
return socket.getPreKeys(recipient, unidentifiedAccess, deviceId);
|
return socket.getPreKeys(recipient, unidentifiedAccess, deviceId);
|
||||||
} catch (NonSuccessfulResponseCodeException e) {
|
} catch (NonSuccessfulResponseCodeException e) {
|
||||||
if (e.getCode() == 401 && story) {
|
if (e.getCode() == 401 && story) {
|
||||||
|
Log.d(TAG, "Got 401 when fetching prekey for story. Trying without UD.");
|
||||||
return socket.getPreKeys(recipient, Optional.empty(), deviceId);
|
return socket.getPreKeys(recipient, Optional.empty(), deviceId);
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -21,12 +21,19 @@ public class UnidentifiedAccess {
|
|||||||
|
|
||||||
private final byte[] unidentifiedAccessKey;
|
private final byte[] unidentifiedAccessKey;
|
||||||
private final SenderCertificate unidentifiedCertificate;
|
private final SenderCertificate unidentifiedCertificate;
|
||||||
|
private final boolean isUnrestrictedForStory;
|
||||||
|
|
||||||
public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate)
|
/**
|
||||||
|
* @param isUnrestrictedForStory When sending to a story, we always want to use sealed sender. Receivers will accept it for story messages. However, there are
|
||||||
|
* some situations where we need to know if this access key will be correct for non-story purposes. Set this flag to true if
|
||||||
|
* the access key is a synthetic one that would only be valid for story messages.
|
||||||
|
*/
|
||||||
|
public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate, boolean isUnrestrictedForStory)
|
||||||
throws InvalidCertificateException
|
throws InvalidCertificateException
|
||||||
{
|
{
|
||||||
this.unidentifiedAccessKey = unidentifiedAccessKey;
|
this.unidentifiedAccessKey = unidentifiedAccessKey;
|
||||||
this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate);
|
this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate);
|
||||||
|
this.isUnrestrictedForStory = isUnrestrictedForStory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getUnidentifiedAccessKey() {
|
public byte[] getUnidentifiedAccessKey() {
|
||||||
@@ -37,6 +44,10 @@ public class UnidentifiedAccess {
|
|||||||
return unidentifiedCertificate;
|
return unidentifiedCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUnrestrictedForStory() {
|
||||||
|
return isUnrestrictedForStory;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] deriveAccessKeyFrom(ProfileKey profileKey) {
|
public static byte[] deriveAccessKeyFrom(ProfileKey profileKey) {
|
||||||
try {
|
try {
|
||||||
byte[] nonce = new byte[12];
|
byte[] nonce = new byte[12];
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class SignalClient {
|
|||||||
|
|
||||||
val outgoingPushMessage: OutgoingPushMessage = cipher.encrypt(
|
val outgoingPushMessage: OutgoingPushMessage = cipher.encrypt(
|
||||||
SignalProtocolAddress(to.serviceId.toString(), 1),
|
SignalProtocolAddress(to.serviceId.toString(), 1),
|
||||||
Optional.of(UnidentifiedAccess(to.unidentifiedAccessKey, senderCertificate.serialized)),
|
Optional.of(UnidentifiedAccess(to.unidentifiedAccessKey, senderCertificate.serialized, false)),
|
||||||
EnvelopeContent.encrypted(content, ContentHint.RESENDABLE, Optional.empty())
|
EnvelopeContent.encrypted(content, ContentHint.RESENDABLE, Optional.empty())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user