1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.config
.ServiceConfig
;
4 import org
.asamk
.signal
.manager
.internal
.SignalDependencies
;
5 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
6 import org
.asamk
.signal
.manager
.util
.KeyUtils
;
7 import org
.signal
.libsignal
.protocol
.IdentityKeyPair
;
8 import org
.signal
.libsignal
.protocol
.InvalidKeyIdException
;
9 import org
.signal
.libsignal
.protocol
.state
.KyberPreKeyRecord
;
10 import org
.signal
.libsignal
.protocol
.state
.PreKeyRecord
;
11 import org
.signal
.libsignal
.protocol
.state
.SignedPreKeyRecord
;
12 import org
.slf4j
.Logger
;
13 import org
.slf4j
.LoggerFactory
;
14 import org
.whispersystems
.signalservice
.api
.account
.PreKeyUpload
;
15 import org
.whispersystems
.signalservice
.api
.push
.ServiceIdType
;
16 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.AuthorizationFailedException
;
17 import org
.whispersystems
.signalservice
.internal
.push
.OneTimePreKeyCounts
;
19 import java
.io
.IOException
;
20 import java
.util
.List
;
22 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.PREKEY_STALE_AGE
;
23 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.SIGNED_PREKEY_ROTATE_AGE
;
25 public class PreKeyHelper
{
27 private static final Logger logger
= LoggerFactory
.getLogger(PreKeyHelper
.class);
29 private final SignalAccount account
;
30 private final SignalDependencies dependencies
;
33 final SignalAccount account
, final SignalDependencies dependencies
35 this.account
= account
;
36 this.dependencies
= dependencies
;
39 public void refreshPreKeysIfNecessary() throws IOException
{
40 refreshPreKeysIfNecessary(ServiceIdType
.ACI
);
41 refreshPreKeysIfNecessary(ServiceIdType
.PNI
);
44 public void refreshPreKeysIfNecessary(ServiceIdType serviceIdType
) throws IOException
{
45 final var identityKeyPair
= account
.getIdentityKeyPair(serviceIdType
);
46 if (identityKeyPair
== null) {
49 final var accountId
= account
.getAccountId(serviceIdType
);
50 if (accountId
== null) {
54 if (refreshPreKeysIfNecessary(serviceIdType
, identityKeyPair
)) {
55 refreshPreKeysIfNecessary(serviceIdType
, identityKeyPair
);
59 private boolean refreshPreKeysIfNecessary(
60 final ServiceIdType serviceIdType
, final IdentityKeyPair identityKeyPair
61 ) throws IOException
{
62 OneTimePreKeyCounts preKeyCounts
;
64 preKeyCounts
= dependencies
.getAccountManager().getPreKeyCounts(serviceIdType
);
65 } catch (AuthorizationFailedException e
) {
66 logger
.debug("Failed to get pre key count, ignoring: " + e
.getClass().getSimpleName());
67 preKeyCounts
= new OneTimePreKeyCounts(0, 0);
70 List
<PreKeyRecord
> preKeyRecords
= null;
71 if (preKeyCounts
.getEcCount() < ServiceConfig
.PREKEY_MINIMUM_COUNT
) {
72 logger
.debug("Refreshing {} ec pre keys, because only {} of min {} pre keys remain",
74 preKeyCounts
.getEcCount(),
75 ServiceConfig
.PREKEY_MINIMUM_COUNT
);
76 preKeyRecords
= generatePreKeys(serviceIdType
);
79 SignedPreKeyRecord signedPreKeyRecord
= null;
80 if (signedPreKeyNeedsRefresh(serviceIdType
)) {
81 logger
.debug("Refreshing {} signed pre key.", serviceIdType
);
82 signedPreKeyRecord
= generateSignedPreKey(serviceIdType
, identityKeyPair
);
85 List
<KyberPreKeyRecord
> kyberPreKeyRecords
= null;
86 if (preKeyCounts
.getKyberCount() < ServiceConfig
.PREKEY_MINIMUM_COUNT
) {
87 logger
.debug("Refreshing {} kyber pre keys, because only {} of min {} pre keys remain",
89 preKeyCounts
.getKyberCount(),
90 ServiceConfig
.PREKEY_MINIMUM_COUNT
);
91 kyberPreKeyRecords
= generateKyberPreKeys(serviceIdType
, identityKeyPair
);
94 KyberPreKeyRecord lastResortKyberPreKeyRecord
= null;
95 if (lastResortKyberPreKeyNeedsRefresh(serviceIdType
)) {
96 logger
.debug("Refreshing {} last resort kyber pre key.", serviceIdType
);
97 lastResortKyberPreKeyRecord
= generateLastResortKyberPreKey(serviceIdType
, identityKeyPair
);
100 if (signedPreKeyRecord
== null
101 && preKeyRecords
== null
102 && lastResortKyberPreKeyRecord
== null
103 && kyberPreKeyRecords
== null) {
107 final var preKeyUpload
= new PreKeyUpload(serviceIdType
,
110 lastResortKyberPreKeyRecord
,
112 var needsReset
= false;
114 dependencies
.getAccountManager().setPreKeys(preKeyUpload
);
116 if (preKeyRecords
!= null) {
117 account
.addPreKeys(serviceIdType
, preKeyRecords
);
119 if (signedPreKeyRecord
!= null) {
120 account
.addSignedPreKey(serviceIdType
, signedPreKeyRecord
);
122 } catch (Exception e
) {
123 logger
.warn("Failed to store new pre keys, resetting preKey id offset", e
);
124 account
.resetPreKeyOffsets(serviceIdType
);
128 if (kyberPreKeyRecords
!= null) {
129 account
.addKyberPreKeys(serviceIdType
, kyberPreKeyRecords
);
131 if (lastResortKyberPreKeyRecord
!= null) {
132 account
.addLastResortKyberPreKey(serviceIdType
, lastResortKyberPreKeyRecord
);
134 } catch (Exception e
) {
135 logger
.warn("Failed to store new kyber pre keys, resetting preKey id offset", e
);
136 account
.resetKyberPreKeyOffsets(serviceIdType
);
139 } catch (AuthorizationFailedException e
) {
140 // This can happen when the primary device has changed phone number
141 logger
.warn("Failed to updated pre keys: {}", e
.getMessage());
146 public void cleanOldPreKeys() {
147 cleanOldPreKeys(ServiceIdType
.ACI
);
148 cleanOldPreKeys(ServiceIdType
.PNI
);
151 private void cleanOldPreKeys(final ServiceIdType serviceIdType
) {
152 cleanSignedPreKeys(serviceIdType
);
153 cleanOneTimePreKeys(serviceIdType
);
156 private List
<PreKeyRecord
> generatePreKeys(ServiceIdType serviceIdType
) {
157 final var accountData
= account
.getAccountData(serviceIdType
);
158 final var offset
= accountData
.getPreKeyMetadata().getNextPreKeyId();
160 var records
= KeyUtils
.generatePreKeyRecords(offset
);
165 private boolean signedPreKeyNeedsRefresh(ServiceIdType serviceIdType
) {
166 final var accountData
= account
.getAccountData(serviceIdType
);
168 final var activeSignedPreKeyId
= accountData
.getPreKeyMetadata().getActiveSignedPreKeyId();
169 if (activeSignedPreKeyId
== -1) {
173 final var signedPreKeyRecord
= accountData
.getSignedPreKeyStore().loadSignedPreKey(activeSignedPreKeyId
);
174 return signedPreKeyRecord
.getTimestamp() < System
.currentTimeMillis() - SIGNED_PREKEY_ROTATE_AGE
;
175 } catch (InvalidKeyIdException e
) {
180 private SignedPreKeyRecord
generateSignedPreKey(ServiceIdType serviceIdType
, IdentityKeyPair identityKeyPair
) {
181 final var accountData
= account
.getAccountData(serviceIdType
);
182 final var signedPreKeyId
= accountData
.getPreKeyMetadata().getNextSignedPreKeyId();
184 return KeyUtils
.generateSignedPreKeyRecord(signedPreKeyId
, identityKeyPair
.getPrivateKey());
187 private List
<KyberPreKeyRecord
> generateKyberPreKeys(
188 ServiceIdType serviceIdType
, final IdentityKeyPair identityKeyPair
190 final var accountData
= account
.getAccountData(serviceIdType
);
191 final var offset
= accountData
.getPreKeyMetadata().getNextKyberPreKeyId();
193 return KeyUtils
.generateKyberPreKeyRecords(offset
, identityKeyPair
.getPrivateKey());
196 private boolean lastResortKyberPreKeyNeedsRefresh(ServiceIdType serviceIdType
) {
197 final var accountData
= account
.getAccountData(serviceIdType
);
199 final var activeLastResortKyberPreKeyId
= accountData
.getPreKeyMetadata().getActiveLastResortKyberPreKeyId();
200 if (activeLastResortKyberPreKeyId
== -1) {
204 final var kyberPreKeyRecord
= accountData
.getKyberPreKeyStore()
205 .loadKyberPreKey(activeLastResortKyberPreKeyId
);
206 return kyberPreKeyRecord
.getTimestamp() < System
.currentTimeMillis() - SIGNED_PREKEY_ROTATE_AGE
;
207 } catch (InvalidKeyIdException e
) {
212 private KyberPreKeyRecord
generateLastResortKyberPreKey(
213 ServiceIdType serviceIdType
, IdentityKeyPair identityKeyPair
215 final var accountData
= account
.getAccountData(serviceIdType
);
216 final var signedPreKeyId
= accountData
.getPreKeyMetadata().getNextKyberPreKeyId();
218 return KeyUtils
.generateKyberPreKeyRecord(signedPreKeyId
, identityKeyPair
.getPrivateKey());
221 private void cleanSignedPreKeys(ServiceIdType serviceIdType
) {
222 final var accountData
= account
.getAccountData(serviceIdType
);
224 final var activeSignedPreKeyId
= accountData
.getPreKeyMetadata().getActiveSignedPreKeyId();
225 accountData
.getSignedPreKeyStore().removeOldSignedPreKeys(activeSignedPreKeyId
);
227 final var activeLastResortKyberPreKeyId
= accountData
.getPreKeyMetadata().getActiveLastResortKyberPreKeyId();
228 accountData
.getKyberPreKeyStore().removeOldLastResortKyberPreKeys(activeLastResortKyberPreKeyId
);
231 private void cleanOneTimePreKeys(ServiceIdType serviceIdType
) {
232 long threshold
= System
.currentTimeMillis() - PREKEY_STALE_AGE
;
235 final var accountData
= account
.getAccountData(serviceIdType
);
236 accountData
.getPreKeyStore().deleteAllStaleOneTimeEcPreKeys(threshold
, minCount
);
237 accountData
.getKyberPreKeyStore().deleteAllStaleOneTimeKyberPreKeys(threshold
, minCount
);