diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index cc1df94b69..5b422f11bd 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -205,6 +205,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { throws IOException, RecipientFormattingException, InvalidNumberException, UndeliverableMessageException, UntrustedIdentityException { + rotateSenderCertificateIfNecessary(); + String groupId = message.getRecipient().getAddress().toGroupString(); Optional profileKey = getProfileKey(message.getRecipient()); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index a1bfe52a9b..2f88cc2ec3 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -158,6 +158,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { } try { + rotateSenderCertificateIfNecessary(); + SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress()); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); List scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments()); diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index c1a179b387..1e53376684 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -4,6 +4,8 @@ import android.content.Context; import android.support.annotation.NonNull; import org.greenrobot.eventbus.EventBus; +import org.signal.libsignal.metadata.certificate.InvalidCertificateException; +import org.signal.libsignal.metadata.certificate.SenderCertificate; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.TextSecureExpiredException; import org.thoughtcrime.securesms.attachments.Attachment; @@ -40,8 +42,9 @@ import java.util.concurrent.TimeUnit; public abstract class PushSendJob extends SendJob { - private static final long serialVersionUID = 5906098204770900739L; - private static final String TAG = PushSendJob.class.getSimpleName(); + private static final long serialVersionUID = 5906098204770900739L; + private static final String TAG = PushSendJob.class.getSimpleName(); + private static final long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.SECONDS.toMillis(30); protected PushSendJob(Context context, JobParameters parameters) { super(context, parameters); @@ -198,5 +201,23 @@ public abstract class PushSendJob extends SendJob { return sharedContacts; } + protected void rotateSenderCertificateIfNecessary() throws IOException { + try { + SenderCertificate certificate = new SenderCertificate(TextSecurePreferences.getUnidentifiedAccessCertificate(context)); + + if (System.currentTimeMillis() > (certificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER)) { + throw new InvalidCertificateException("Certificate is expired."); + } + + Log.d(TAG, "Certificate is valid."); + } catch (InvalidCertificateException e) { + Log.w(TAG, "Certificate was invalid at send time. Fetching a new one.", e); + RotateCertificateJob certificateJob = new RotateCertificateJob(); + ApplicationContext.getInstance(context).injectDependencies(certificateJob); + certificateJob.setContext(context); + certificateJob.onRun(); + } + } + protected abstract void onPushSend() throws Exception; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 3845728ed8..d1f6db9365 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -151,6 +151,8 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException { try { + rotateSenderCertificateIfNecessary(); + SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress()); Optional profileKey = getProfileKey(message.getIndividualRecipient()); Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient()); diff --git a/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java b/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java index 15f9bd94f0..94ab124beb 100644 --- a/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java @@ -55,8 +55,10 @@ public class RotateCertificateJob extends ContextJob implements InjectableType { @Override public void onRun() throws IOException { - byte[] certificate = accountManager.getSenderCertificate(); - TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate); + synchronized (RotateCertificateJob.class) { + byte[] certificate = accountManager.getSenderCertificate(); + TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate); + } } @Override