That fuck shit the fascists are using
at master 221 lines 8.8 kB view raw
1package org.tm.archive.crypto; 2 3 4import android.content.Context; 5 6import androidx.annotation.NonNull; 7import androidx.annotation.Nullable; 8import androidx.annotation.WorkerThread; 9 10import com.annimon.stream.Stream; 11 12import org.signal.core.util.logging.Log; 13import org.signal.libsignal.metadata.certificate.CertificateValidator; 14import org.signal.libsignal.metadata.certificate.InvalidCertificateException; 15import org.signal.libsignal.protocol.InvalidKeyException; 16import org.signal.libsignal.protocol.ecc.Curve; 17import org.signal.libsignal.protocol.ecc.ECPublicKey; 18import org.signal.libsignal.zkgroup.profiles.ProfileKey; 19import org.tm.archive.BuildConfig; 20import org.tm.archive.keyvalue.CertificateType; 21import org.tm.archive.keyvalue.SignalStore; 22import org.tm.archive.recipients.Recipient; 23import org.tm.archive.recipients.RecipientId; 24import org.signal.core.util.Base64; 25import org.tm.archive.util.TextSecurePreferences; 26import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; 27import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; 28 29import java.io.IOException; 30import java.util.Collections; 31import java.util.HashMap; 32import java.util.Iterator; 33import java.util.List; 34import java.util.Map; 35import java.util.Optional; 36import java.util.stream.Collectors; 37 38public class UnidentifiedAccessUtil { 39 40 private static final String TAG = Log.tag(UnidentifiedAccessUtil.class); 41 42 private static final byte[] UNRESTRICTED_KEY = new byte[16]; 43 44 public static CertificateValidator getCertificateValidator() { 45 return CertificateValidatorHolder.INSTANCE.certificateValidator; 46 } 47 48 @WorkerThread 49 public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context, @NonNull Recipient recipient) { 50 return getAccessFor(context, recipient, true); 51 } 52 53 @WorkerThread 54 public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context, @NonNull Recipient recipient, boolean log) { 55 return getAccessFor(context, Collections.singletonList(recipient), log).get(0); 56 } 57 58 @WorkerThread 59 public static List<Optional<UnidentifiedAccessPair>> getAccessFor(@NonNull Context context, @NonNull List<Recipient> recipients) { 60 return getAccessFor(context, recipients, true); 61 } 62 63 @WorkerThread 64 public static Map<RecipientId, Optional<UnidentifiedAccessPair>> getAccessMapFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean isForStory) { 65 List<Optional<UnidentifiedAccessPair>> accessList = getAccessFor(context, recipients, isForStory, true); 66 67 Iterator<Recipient> recipientIterator = recipients.iterator(); 68 Iterator<Optional<UnidentifiedAccessPair>> accessIterator = accessList.iterator(); 69 70 Map<RecipientId, Optional<UnidentifiedAccessPair>> accessMap = new HashMap<>(recipients.size()); 71 72 while (recipientIterator.hasNext()) { 73 accessMap.put(recipientIterator.next().getId(), accessIterator.next()); 74 } 75 76 return accessMap; 77 } 78 79 @WorkerThread 80 public static List<Optional<UnidentifiedAccessPair>> getAccessFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean log) { 81 return getAccessFor(context, recipients, false, log); 82 } 83 84 @WorkerThread 85 public static List<Optional<UnidentifiedAccessPair>> getAccessFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean isForStory, boolean log) { 86 final byte[] ourUnidentifiedAccessKey; 87 88 if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { 89 ourUnidentifiedAccessKey = UNRESTRICTED_KEY; 90 } else { 91 ourUnidentifiedAccessKey = ProfileKeyUtil.getSelfProfileKey().deriveAccessKey(); 92 } 93 94 CertificateType certificateType = getUnidentifiedAccessCertificateType(); 95 byte[] ourUnidentifiedAccessCertificate = SignalStore.certificateValues().getUnidentifiedAccessCertificate(certificateType); 96 97 List<Optional<UnidentifiedAccessPair>> access = recipients.parallelStream().map(recipient -> { 98 UnidentifiedAccessPair unidentifiedAccessPair = null; 99 if (ourUnidentifiedAccessCertificate != null) { 100 try { 101 UnidentifiedAccess theirAccess = getTargetUnidentifiedAccess(recipient, ourUnidentifiedAccessCertificate, isForStory); 102 UnidentifiedAccess ourAccess = new UnidentifiedAccess(ourUnidentifiedAccessKey, ourUnidentifiedAccessCertificate, false); 103 104 if (theirAccess != null) { 105 unidentifiedAccessPair = new UnidentifiedAccessPair(theirAccess, ourAccess); 106 } 107 } catch (InvalidCertificateException e) { 108 Log.w(TAG, "Invalid unidentified access certificate!", e); 109 } 110 } else { 111 Log.w(TAG, "Missing unidentified access certificate!"); 112 } 113 return Optional.ofNullable(unidentifiedAccessPair); 114 }).collect(Collectors.toList()); 115 116 int unidentifiedCount = Stream.of(access).filter(Optional::isPresent).toList().size(); 117 int otherCount = access.size() - unidentifiedCount; 118 119 if (log) { 120 Log.i(TAG, "Unidentified: " + unidentifiedCount + ", Other: " + otherCount); 121 } 122 123 return access; 124 } 125 126 public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) { 127 try { 128 byte[] ourUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey()); 129 byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(); 130 131 if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { 132 ourUnidentifiedAccessKey = UNRESTRICTED_KEY; 133 } 134 135 if (ourUnidentifiedAccessCertificate != null) { 136 return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey, 137 ourUnidentifiedAccessCertificate, 138 false), 139 new UnidentifiedAccess(ourUnidentifiedAccessKey, 140 ourUnidentifiedAccessCertificate, 141 false))); 142 } 143 144 return Optional.empty(); 145 } catch (InvalidCertificateException e) { 146 Log.w(TAG, e); 147 return Optional.empty(); 148 } 149 } 150 151 private static @NonNull CertificateType getUnidentifiedAccessCertificateType() { 152 if (SignalStore.phoneNumberPrivacy().isPhoneNumberSharingEnabled()) { 153 return CertificateType.ACI_AND_E164; 154 } else { 155 return CertificateType.ACI_ONLY; 156 } 157 } 158 159 private static byte[] getUnidentifiedAccessCertificate() { 160 return SignalStore.certificateValues() 161 .getUnidentifiedAccessCertificate(getUnidentifiedAccessCertificateType()); 162 } 163 164 private static @Nullable UnidentifiedAccess getTargetUnidentifiedAccess(@NonNull Recipient recipient, @NonNull byte[] certificate, boolean isForStory) throws InvalidCertificateException { 165 ProfileKey theirProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey()); 166 167 byte[] accessKey; 168 169 switch (recipient.resolve().getUnidentifiedAccessMode()) { 170 case UNKNOWN: 171 if (theirProfileKey == null) { 172 if (isForStory) { 173 accessKey = null; 174 } else { 175 accessKey = UNRESTRICTED_KEY; 176 } 177 } else { 178 accessKey = theirProfileKey.deriveAccessKey(); 179 } 180 break; 181 case DISABLED: 182 accessKey = null; 183 break; 184 case ENABLED: 185 if (theirProfileKey == null) { 186 accessKey = null; 187 } else { 188 accessKey = theirProfileKey.deriveAccessKey(); 189 } 190 break; 191 case UNRESTRICTED: 192 accessKey = UNRESTRICTED_KEY; 193 break; 194 default: 195 throw new AssertionError("Unknown mode: " + recipient.getUnidentifiedAccessMode().getMode()); 196 } 197 198 if (accessKey == null && isForStory) { 199 return new UnidentifiedAccess(UNRESTRICTED_KEY, certificate, true); 200 } else if (accessKey != null) { 201 return new UnidentifiedAccess(accessKey, certificate, false); 202 } else { 203 return null; 204 } 205 } 206 207 private enum CertificateValidatorHolder { 208 INSTANCE; 209 210 final CertificateValidator certificateValidator = buildCertificateValidator(); 211 212 private static CertificateValidator buildCertificateValidator() { 213 try { 214 ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BuildConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0); 215 return new CertificateValidator(unidentifiedSenderTrustRoot); 216 } catch (InvalidKeyException | IOException e) { 217 throw new AssertionError(e); 218 } 219 } 220 } 221}