That fuck shit the fascists are using
1package org.tm.archive.keyvalue;
2
3import androidx.annotation.NonNull;
4import androidx.annotation.Nullable;
5
6import org.signal.core.util.StringStringSerializer;
7import org.tm.archive.util.JsonUtils;
8import org.whispersystems.signalservice.api.kbs.MasterKey;
9import org.whispersystems.signalservice.api.kbs.PinHashUtil;
10import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
11
12import java.io.IOException;
13import java.security.SecureRandom;
14import java.util.ArrayList;
15import java.util.List;
16import java.util.stream.Collectors;
17import java.util.stream.Stream;
18
19public final class SvrValues extends SignalStoreValues {
20
21 public static final String REGISTRATION_LOCK_ENABLED = "kbs.v2_lock_enabled";
22 private static final String MASTER_KEY = "kbs.registration_lock_master_key";
23 private static final String TOKEN_RESPONSE = "kbs.token_response";
24 private static final String PIN = "kbs.pin";
25 private static final String LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash";
26 private static final String LAST_CREATE_FAILED_TIMESTAMP = "kbs.last_create_failed_timestamp";
27 public static final String OPTED_OUT = "kbs.opted_out";
28 private static final String PIN_FORGOTTEN_OR_SKIPPED = "kbs.pin.forgotten.or.skipped";
29 private static final String SVR_AUTH_TOKENS = "kbs.kbs_auth_tokens";
30 private static final String SVR_LAST_AUTH_REFRESH_TIMESTAMP = "kbs.kbs_auth_tokens.last_refresh_timestamp";
31
32 SvrValues(KeyValueStore store) {
33 super(store);
34 }
35
36 @Override
37 void onFirstEverAppLaunch() {
38 }
39
40 @Override
41 @NonNull
42 List<String> getKeysToIncludeInBackup() {
43 return List.of(SVR_AUTH_TOKENS);
44 }
45
46 /**
47 * Deliberately does not clear the {@link #MASTER_KEY}.
48 */
49 public void clearRegistrationLockAndPin() {
50 getStore().beginWrite()
51 .remove(REGISTRATION_LOCK_ENABLED)
52 .remove(TOKEN_RESPONSE)
53 .remove(LOCK_LOCAL_PIN_HASH)
54 .remove(PIN)
55 .remove(LAST_CREATE_FAILED_TIMESTAMP)
56 .remove(OPTED_OUT)
57 .remove(SVR_AUTH_TOKENS)
58 .remove(SVR_LAST_AUTH_REFRESH_TIMESTAMP)
59 .commit();
60 }
61
62 public synchronized void setMasterKey(@NonNull MasterKey masterKey, @NonNull String pin) {
63 getStore().beginWrite()
64 .putBlob(MASTER_KEY, masterKey.serialize())
65 .putString(LOCK_LOCAL_PIN_HASH, PinHashUtil.localPinHash(pin))
66 .putString(PIN, pin)
67 .putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
68 .putBoolean(OPTED_OUT, false)
69 .commit();
70 }
71
72 synchronized void setPinIfNotPresent(@NonNull String pin) {
73 if (getStore().getString(PIN, null) == null) {
74 getStore().beginWrite().putString(PIN, pin).commit();
75 }
76 }
77
78 public synchronized void setRegistrationLockEnabled(boolean enabled) {
79 putBoolean(REGISTRATION_LOCK_ENABLED, enabled);
80 }
81
82 /**
83 * Whether or not registration lock V2 is enabled.
84 */
85 public synchronized boolean isRegistrationLockEnabled() {
86 return getBoolean(REGISTRATION_LOCK_ENABLED, false);
87 }
88
89 public synchronized void onPinCreateFailure() {
90 putLong(LAST_CREATE_FAILED_TIMESTAMP, System.currentTimeMillis());
91 }
92
93 /**
94 * Whether or not the last time the user attempted to create a PIN, it failed.
95 */
96 public synchronized boolean lastPinCreateFailed() {
97 return getLong(LAST_CREATE_FAILED_TIMESTAMP, -1) > 0;
98 }
99
100 /**
101 * Finds or creates the master key. Therefore this will always return a master key whether backed
102 * up or not.
103 * <p>
104 * If you only want a key when it's backed up, use {@link #getPinBackedMasterKey()}.
105 */
106 public synchronized @NonNull MasterKey getOrCreateMasterKey() {
107 byte[] blob = getStore().getBlob(MASTER_KEY, null);
108
109 if (blob == null) {
110 getStore().beginWrite()
111 .putBlob(MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
112 .commit();
113 blob = getBlob(MASTER_KEY, null);
114 }
115
116 return new MasterKey(blob);
117 }
118
119 /**
120 * Returns null if master key is not backed up by a pin.
121 */
122 public synchronized @Nullable MasterKey getPinBackedMasterKey() {
123 if (!isRegistrationLockEnabled()) return null;
124 return getMasterKey();
125 }
126
127 private synchronized @Nullable MasterKey getMasterKey() {
128 byte[] blob = getBlob(MASTER_KEY, null);
129 return blob != null ? new MasterKey(blob) : null;
130 }
131
132 public @Nullable String getRegistrationLockToken() {
133 MasterKey masterKey = getPinBackedMasterKey();
134 if (masterKey == null) {
135 return null;
136 } else {
137 return masterKey.deriveRegistrationLock();
138 }
139 }
140
141 public synchronized @Nullable String getRecoveryPassword() {
142 MasterKey masterKey = getMasterKey();
143 if (masterKey != null && hasPin()) {
144 return masterKey.deriveRegistrationRecoveryPassword();
145 } else {
146 return null;
147 }
148 }
149
150 public synchronized @Nullable String getPin() {
151 return getString(PIN, null);
152 }
153
154 public synchronized @Nullable String getLocalPinHash() {
155 return getString(LOCK_LOCAL_PIN_HASH, null);
156 }
157
158 public synchronized boolean hasPin() {
159 return getLocalPinHash() != null;
160 }
161
162 public synchronized boolean isPinForgottenOrSkipped() {
163 return getBoolean(PIN_FORGOTTEN_OR_SKIPPED, false);
164 }
165
166 public synchronized void setPinForgottenOrSkipped(boolean value) {
167 putBoolean(PIN_FORGOTTEN_OR_SKIPPED, value);
168 }
169
170 public synchronized void putAuthTokenList(List<String> tokens) {
171 putList(SVR_AUTH_TOKENS, tokens, StringStringSerializer.INSTANCE);
172 setLastRefreshAuthTimestamp(System.currentTimeMillis());
173 }
174
175 public synchronized List<String> getAuthTokenList() {
176 return getList(SVR_AUTH_TOKENS, StringStringSerializer.INSTANCE);
177 }
178
179 /**
180 * Keeps the 10 most recent KBS auth tokens.
181 * @param token
182 * @return whether the token was added (new) or ignored (already existed)
183 */
184 public synchronized boolean appendAuthTokenToList(String token) {
185 List<String> tokens = getAuthTokenList();
186 if (tokens.contains(token)) {
187 return false;
188 } else {
189 final List<String> result = Stream.concat(Stream.of(token), tokens.stream()).limit(10).collect(Collectors.toList());
190 putAuthTokenList(result);
191 return true;
192 }
193 }
194
195 public boolean removeAuthTokens(@NonNull List<String> invalid) {
196 List<String> tokens = new ArrayList<>(getAuthTokenList());
197 if (tokens.removeAll(invalid)) {
198 putAuthTokenList(tokens);
199 return true;
200 }
201
202 return false;
203 }
204
205 public synchronized void optOut() {
206 getStore().beginWrite()
207 .putBoolean(OPTED_OUT, true)
208 .remove(TOKEN_RESPONSE)
209 .putBlob(MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
210 .remove(LOCK_LOCAL_PIN_HASH)
211 .remove(PIN)
212 .putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
213 .commit();
214 }
215
216 public synchronized boolean hasOptedOut() {
217 return getBoolean(OPTED_OUT, false);
218 }
219
220 public synchronized @Nullable TokenResponse getRegistrationLockTokenResponse() {
221 String token = getStore().getString(TOKEN_RESPONSE, null);
222
223 if (token == null) return null;
224
225 try {
226 return JsonUtils.fromJson(token, TokenResponse.class);
227 } catch (IOException e) {
228 throw new AssertionError(e);
229 }
230 }
231
232 private void setLastRefreshAuthTimestamp(long timestamp) {
233 putLong(SVR_LAST_AUTH_REFRESH_TIMESTAMP, timestamp);
234 }
235
236 public long getLastRefreshAuthTimestamp() {
237 return getLong(SVR_LAST_AUTH_REFRESH_TIMESTAMP, 0L);
238 }
239}