mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-21 22:11:38 +00:00
clean up signal service protocols
This commit is contained in:
parent
1db9ccdf27
commit
8c016b3802
@ -99,8 +99,6 @@ import org.session.libsignal.service.loki.api.SnodeAPI;
|
|||||||
import org.session.libsignal.service.loki.api.SwarmAPI;
|
import org.session.libsignal.service.loki.api.SwarmAPI;
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
|
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI;
|
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI;
|
||||||
import org.session.libsignal.service.loki.api.shelved.p2p.LokiP2PAPI;
|
|
||||||
import org.session.libsignal.service.loki.api.shelved.p2p.LokiP2PAPIDelegate;
|
|
||||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
|
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
|
||||||
|
|
||||||
@ -127,7 +125,7 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
|||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate {
|
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver {
|
||||||
|
|
||||||
private static final String TAG = ApplicationContext.class.getSimpleName();
|
private static final String TAG = ApplicationContext.class.getSimpleName();
|
||||||
|
|
||||||
@ -183,7 +181,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
||||||
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
|
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
|
||||||
}
|
}
|
||||||
setUpP2PAPIIfNeeded();
|
|
||||||
PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
||||||
setUpStorageAPIIfNeeded();
|
setUpStorageAPIIfNeeded();
|
||||||
resubmitProfilePictureIfNeeded();
|
resubmitProfilePictureIfNeeded();
|
||||||
@ -423,22 +420,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
public boolean setUpStorageAPIIfNeeded() {
|
public boolean setUpStorageAPIIfNeeded() {
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; }
|
if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; }
|
||||||
boolean isDebugMode = BuildConfig.DEBUG;
|
|
||||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||||
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUpP2PAPIIfNeeded() {
|
|
||||||
String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
|
||||||
if (hexEncodedPublicKey == null) { return; }
|
|
||||||
LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> {
|
|
||||||
// TODO: Implement
|
|
||||||
return null;
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerForFCMIfNeeded(Boolean force) {
|
public void registerForFCMIfNeeded(Boolean force) {
|
||||||
Context context = this;
|
Context context = this;
|
||||||
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
|
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
|
||||||
@ -457,11 +444,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ping(@NotNull String s) {
|
|
||||||
// TODO: Implement
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUpPollingIfNeeded() {
|
private void setUpPollingIfNeeded() {
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userPublicKey == null) return;
|
if (userPublicKey == null) return;
|
||||||
|
@ -34,14 +34,12 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
|
|||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.SendMessageResult;
|
import org.session.libsignal.service.api.messages.SendMessageResult;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview;
|
import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview;
|
||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException;
|
|
||||||
import org.session.libsignal.service.loki.api.SnodeAPI;
|
import org.session.libsignal.service.loki.api.SnodeAPI;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@ -300,9 +298,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
return isUnidentified;
|
return isUnidentified;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
warn(TAG, e);
|
|
||||||
throw new InsecureFallbackApprovalException(e);
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
warn(TAG, e);
|
warn(TAG, e);
|
||||||
throw new UndeliverableMessageException(e);
|
throw new UndeliverableMessageException(e);
|
||||||
|
@ -30,7 +30,6 @@ import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|||||||
import org.session.libsignal.service.api.messages.SendMessageResult;
|
import org.session.libsignal.service.api.messages.SendMessageResult;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException;
|
|
||||||
import org.session.libsignal.service.loki.api.SnodeAPI;
|
import org.session.libsignal.service.loki.api.SnodeAPI;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -240,9 +239,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
return isUnidentified;
|
return isUnidentified;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
warn(TAG, "Failure", e);
|
|
||||||
throw new InsecureFallbackApprovalException(e);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
warn(TAG, "Failure", e);
|
warn(TAG, "Failure", e);
|
||||||
throw new RetryLaterException(e);
|
throw new RetryLaterException(e);
|
||||||
|
@ -191,7 +191,6 @@ class BackupRestoreViewModel(application: Application): AndroidViewModel(applica
|
|||||||
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
||||||
val application = ApplicationContext.getInstance(context)
|
val application = ApplicationContext.getInstance(context)
|
||||||
application.setUpStorageAPIIfNeeded()
|
application.setUpStorageAPIIfNeeded()
|
||||||
application.setUpP2PAPIIfNeeded()
|
|
||||||
|
|
||||||
BackupRestoreResult.SUCCESS
|
BackupRestoreResult.SUCCESS
|
||||||
} catch (e: DatabaseDowngradeException) {
|
} catch (e: DatabaseDowngradeException) {
|
||||||
|
@ -9,11 +9,11 @@ import android.widget.TextView.OnEditorActionListener
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_display_name.*
|
import kotlinx.android.synthetic.main.activity_display_name.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
import org.thoughtcrime.securesms.loki.utilities.push
|
||||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.api.crypto.ProfileCipher
|
|
||||||
|
|
||||||
class DisplayNameActivity : BaseActionBarActivity() {
|
class DisplayNameActivity : BaseActionBarActivity() {
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
|||||||
if (displayName.isEmpty()) {
|
if (displayName.isEmpty()) {
|
||||||
return Toast.makeText(this, R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
return Toast.makeText(this, R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) {
|
if (displayName.toByteArray().size > ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH) {
|
||||||
return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
@ -154,7 +154,6 @@ class PNModeActivity : BaseActionBarActivity() {
|
|||||||
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))
|
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))
|
||||||
val application = ApplicationContext.getInstance(this)
|
val application = ApplicationContext.getInstance(this)
|
||||||
application.setUpStorageAPIIfNeeded()
|
application.setUpStorageAPIIfNeeded()
|
||||||
application.setUpP2PAPIIfNeeded()
|
|
||||||
val intent = Intent(this, HomeActivity::class.java)
|
val intent = Intent(this, HomeActivity::class.java)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
show(intent)
|
show(intent)
|
||||||
|
@ -41,11 +41,11 @@ import org.thoughtcrime.securesms.mms.GlideApp
|
|||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.session.libsession.messaging.avatars.AvatarHelper
|
import org.session.libsession.messaging.avatars.AvatarHelper
|
||||||
|
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.api.crypto.ProfileCipher
|
|
||||||
import org.session.libsignal.service.api.util.StreamDetails
|
import org.session.libsignal.service.api.util.StreamDetails
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@ -225,7 +225,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) {
|
if (displayName.toByteArray().size > ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH) {
|
||||||
Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@ class SSKEnvironment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ProfileManagerProtocol {
|
interface ProfileManagerProtocol {
|
||||||
|
companion object {
|
||||||
|
const val NAME_PADDED_LENGTH = 26
|
||||||
|
}
|
||||||
|
|
||||||
fun setDisplayName(context: Context, recipient: Recipient, displayName: String)
|
fun setDisplayName(context: Context, recipient: Recipient, displayName: String)
|
||||||
fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String)
|
fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String)
|
||||||
fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray)
|
fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray)
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.session.libsignal.libsignal;
|
package org.session.libsignal.libsignal;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class InvalidMessageException extends Exception {
|
public class InvalidMessageException extends Exception {
|
||||||
|
|
||||||
public InvalidMessageException() {}
|
public InvalidMessageException() {}
|
||||||
@ -22,8 +20,4 @@ public class InvalidMessageException extends Exception {
|
|||||||
public InvalidMessageException(String detailMessage, Throwable throwable) {
|
public InvalidMessageException(String detailMessage, Throwable throwable) {
|
||||||
super(detailMessage, throwable);
|
super(detailMessage, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidMessageException(String detailMessage, List<Exception> exceptions) {
|
|
||||||
super(detailMessage, exceptions.get(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,7 @@ package org.session.libsignal.libsignal.ecc;
|
|||||||
|
|
||||||
import org.whispersystems.curve25519.Curve25519;
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
import org.whispersystems.curve25519.VrfSignatureVerificationFailedException;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
import org.session.libsignal.libsignal.InvalidKeyException;
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey;
|
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPublicKey;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECPrivateKey;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECPublicKey;
|
|
||||||
|
|
||||||
import static org.whispersystems.curve25519.Curve25519.BEST;
|
import static org.whispersystems.curve25519.Curve25519.BEST;
|
||||||
|
|
||||||
@ -21,10 +15,6 @@ public class Curve {
|
|||||||
|
|
||||||
public static final int DJB_TYPE = 0x05;
|
public static final int DJB_TYPE = 0x05;
|
||||||
|
|
||||||
public static boolean isNative() {
|
|
||||||
return Curve25519.getInstance(BEST).isNative();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ECKeyPair generateKeyPair() {
|
public static ECKeyPair generateKeyPair() {
|
||||||
Curve25519KeyPair keyPair = Curve25519.getInstance(BEST).generateKeyPair();
|
Curve25519KeyPair keyPair = Curve25519.getInstance(BEST).generateKeyPair();
|
||||||
return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()), new DjbECPrivateKey(keyPair.getPrivateKey()));
|
return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()), new DjbECPrivateKey(keyPair.getPrivateKey()));
|
||||||
@ -56,89 +46,4 @@ public class Curve {
|
|||||||
public static ECPrivateKey decodePrivatePoint(byte[] bytes) {
|
public static ECPrivateKey decodePrivatePoint(byte[] bytes) {
|
||||||
return new DjbECPrivateKey(bytes);
|
return new DjbECPrivateKey(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey)
|
|
||||||
throws InvalidKeyException
|
|
||||||
{
|
|
||||||
if (publicKey == null) {
|
|
||||||
throw new InvalidKeyException("public value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (privateKey == null) {
|
|
||||||
throw new InvalidKeyException("private value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publicKey.getType() != privateKey.getType()) {
|
|
||||||
throw new InvalidKeyException("Public and private keys must be of the same type!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publicKey.getType() == DJB_TYPE) {
|
|
||||||
return Curve25519.getInstance(BEST)
|
|
||||||
.calculateAgreement(((DjbECPublicKey) publicKey).getPublicKey(),
|
|
||||||
((DjbECPrivateKey) privateKey).getPrivateKey());
|
|
||||||
} else {
|
|
||||||
throw new InvalidKeyException("Unknown type: " + publicKey.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature)
|
|
||||||
throws InvalidKeyException
|
|
||||||
{
|
|
||||||
if (signingKey == null || message == null || signature == null) {
|
|
||||||
throw new InvalidKeyException("Values must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signingKey.getType() == DJB_TYPE) {
|
|
||||||
return Curve25519.getInstance(BEST)
|
|
||||||
.verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature);
|
|
||||||
} else {
|
|
||||||
throw new InvalidKeyException("Unknown type: " + signingKey.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message)
|
|
||||||
throws InvalidKeyException
|
|
||||||
{
|
|
||||||
if (signingKey == null || message == null) {
|
|
||||||
throw new InvalidKeyException("Values must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signingKey.getType() == DJB_TYPE) {
|
|
||||||
return Curve25519.getInstance(BEST)
|
|
||||||
.calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message);
|
|
||||||
} else {
|
|
||||||
throw new InvalidKeyException("Unknown type: " + signingKey.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] calculateVrfSignature(ECPrivateKey signingKey, byte[] message)
|
|
||||||
throws InvalidKeyException
|
|
||||||
{
|
|
||||||
if (signingKey == null || message == null) {
|
|
||||||
throw new InvalidKeyException("Values must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signingKey.getType() == DJB_TYPE) {
|
|
||||||
return Curve25519.getInstance(BEST)
|
|
||||||
.calculateVrfSignature(((DjbECPrivateKey)signingKey).getPrivateKey(), message);
|
|
||||||
} else {
|
|
||||||
throw new InvalidKeyException("Unknown type: " + signingKey.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] verifyVrfSignature(ECPublicKey signingKey, byte[] message, byte[] signature)
|
|
||||||
throws InvalidKeyException, VrfSignatureVerificationFailedException
|
|
||||||
{
|
|
||||||
if (signingKey == null || message == null || signature == null) {
|
|
||||||
throw new InvalidKeyException("Values must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signingKey.getType() == DJB_TYPE) {
|
|
||||||
return Curve25519.getInstance(BEST)
|
|
||||||
.verifyVrfSignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature);
|
|
||||||
} else {
|
|
||||||
throw new InvalidKeyException("Unknown type: " + signingKey.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.session.libsignal.libsignal.ecc;
|
package org.session.libsignal.libsignal.ecc;
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECPrivateKey;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECPublicKey;
|
|
||||||
|
|
||||||
public class ECKeyPair {
|
public class ECKeyPair {
|
||||||
|
|
||||||
private final ECPublicKey publicKey;
|
private final ECPublicKey publicKey;
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package org.session.libsignal.libsignal.util;
|
|
||||||
|
|
||||||
public abstract class ByteArrayComparator {
|
|
||||||
|
|
||||||
protected int compare(byte[] left, byte[] right) {
|
|
||||||
for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
|
|
||||||
int a = (left[i] & 0xff);
|
|
||||||
int b = (right[j] & 0xff);
|
|
||||||
|
|
||||||
if (a != b) {
|
|
||||||
return a - b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return left.length - right.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -5,12 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.session.libsignal.libsignal.util;
|
package org.session.libsignal.libsignal.util;
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
|
||||||
import org.session.libsignal.libsignal.ecc.Curve;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
@ -16,14 +16,8 @@
|
|||||||
|
|
||||||
package org.session.libsignal.libsignal.util.guava;
|
package org.session.libsignal.libsignal.util.guava;
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Function;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Supplier;
|
|
||||||
|
|
||||||
import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull;
|
import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -16,12 +16,9 @@
|
|||||||
|
|
||||||
package org.session.libsignal.libsignal.util.guava;
|
package org.session.libsignal.libsignal.util.guava;
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Supplier;
|
|
||||||
|
|
||||||
import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull;
|
import static org.session.libsignal.libsignal.util.guava.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
|
|||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
||||||
import org.session.libsignal.service.api.push.exceptions.UnregisteredUserException;
|
|
||||||
import org.session.libsignal.service.internal.crypto.PaddingInputStream;
|
import org.session.libsignal.service.internal.crypto.PaddingInputStream;
|
||||||
import org.session.libsignal.service.internal.push.OutgoingPushMessage;
|
import org.session.libsignal.service.internal.push.OutgoingPushMessage;
|
||||||
import org.session.libsignal.service.internal.push.OutgoingPushMessageList;
|
import org.session.libsignal.service.internal.push.OutgoingPushMessageList;
|
||||||
@ -486,7 +485,6 @@ public class SignalServiceMessageSender {
|
|||||||
int ttl,
|
int ttl,
|
||||||
boolean isClosedGroup,
|
boolean isClosedGroup,
|
||||||
boolean notifyPNServer)
|
boolean notifyPNServer)
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
List<SendMessageResult> results = new LinkedList<>();
|
List<SendMessageResult> results = new LinkedList<>();
|
||||||
Iterator<SignalServiceAddress> recipientIterator = recipients.iterator();
|
Iterator<SignalServiceAddress> recipientIterator = recipients.iterator();
|
||||||
@ -494,17 +492,8 @@ public class SignalServiceMessageSender {
|
|||||||
|
|
||||||
while (recipientIterator.hasNext()) {
|
while (recipientIterator.hasNext()) {
|
||||||
SignalServiceAddress recipient = recipientIterator.next();
|
SignalServiceAddress recipient = recipientIterator.next();
|
||||||
|
|
||||||
try {
|
|
||||||
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, true, isClosedGroup, notifyPNServer, Optional.absent());
|
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, true, isClosedGroup, notifyPNServer, Optional.absent());
|
||||||
results.add(result);
|
results.add(result);
|
||||||
} catch (UnregisteredUserException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
results.add(SendMessageResult.unregisteredFailure(recipient));
|
|
||||||
} catch (PushNetworkException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
results.add(SendMessageResult.networkFailure(recipient));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@ -534,7 +523,6 @@ public class SignalServiceMessageSender {
|
|||||||
boolean isClosedGroup,
|
boolean isClosedGroup,
|
||||||
boolean notifyPNServer,
|
boolean notifyPNServer,
|
||||||
Optional<String> syncTarget)
|
Optional<String> syncTarget)
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
boolean isSelfSend = syncTarget.isPresent() && !syncTarget.get().isEmpty();
|
boolean isSelfSend = syncTarget.isPresent() && !syncTarget.get().isEmpty();
|
||||||
long threadID;
|
long threadID;
|
||||||
@ -544,17 +532,11 @@ public class SignalServiceMessageSender {
|
|||||||
threadID = threadDatabase.getThreadID(recipient.getNumber());
|
threadID = threadDatabase.getThreadID(recipient.getNumber());
|
||||||
}
|
}
|
||||||
PublicChat publicChat = threadDatabase.getPublicChat(threadID);
|
PublicChat publicChat = threadDatabase.getPublicChat(threadID);
|
||||||
try {
|
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
||||||
return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat);
|
return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat);
|
||||||
} else {
|
} else {
|
||||||
return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, syncTarget);
|
return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, syncTarget);
|
||||||
}
|
}
|
||||||
} catch (PushNetworkException e) {
|
|
||||||
return SendMessageResult.networkFailure(recipient);
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SendMessageResult sendMessageToPublicChat(final long messageID,
|
private SendMessageResult sendMessageToPublicChat(final long messageID,
|
||||||
@ -659,7 +641,6 @@ public class SignalServiceMessageSender {
|
|||||||
boolean isClosedGroup,
|
boolean isClosedGroup,
|
||||||
final boolean notifyPNServer,
|
final boolean notifyPNServer,
|
||||||
Optional<String> syncTarget)
|
Optional<String> syncTarget)
|
||||||
throws IOException, UntrustedIdentityException
|
|
||||||
{
|
{
|
||||||
final SettableFuture<?>[] future = { new SettableFuture<Unit>() };
|
final SettableFuture<?>[] future = { new SettableFuture<Unit>() };
|
||||||
OutgoingPushMessageList messages = getSessionProtocolEncryptedMessage(recipient, timestamp, content);
|
OutgoingPushMessageList messages = getSessionProtocolEncryptedMessage(recipient, timestamp, content);
|
||||||
@ -840,9 +821,4 @@ public class SignalServiceMessageSender {
|
|||||||
|
|
||||||
return new OutgoingPushMessageList(publicKey, timestamp, messages, false);
|
return new OutgoingPushMessageList(publicKey, timestamp, messages, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface EventListener {
|
|
||||||
|
|
||||||
public void onSecurityEvent(SignalServiceAddress address);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,9 @@ package org.session.libsignal.service.api.crypto;
|
|||||||
|
|
||||||
import org.session.libsignal.libsignal.InvalidMacException;
|
import org.session.libsignal.libsignal.InvalidMacException;
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||||
import org.session.libsignal.libsignal.kdf.HKDFv3;
|
|
||||||
import org.session.libsignal.service.internal.util.ContentLengthInputStream;
|
import org.session.libsignal.service.internal.util.ContentLengthInputStream;
|
||||||
import org.session.libsignal.service.internal.util.Util;
|
import org.session.libsignal.service.internal.util.Util;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
@ -86,32 +84,6 @@ public class AttachmentCipherInputStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InputStream createForStickerData(byte[] data, byte[] packKey)
|
|
||||||
throws InvalidMessageException, IOException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
byte[] combinedKeyMaterial = new HKDFv3().deriveSecrets(packKey, "Sticker Pack".getBytes(), 64);
|
|
||||||
byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE);
|
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
|
||||||
mac.init(new SecretKeySpec(parts[1], "HmacSHA256"));
|
|
||||||
|
|
||||||
if (data.length <= BLOCK_SIZE + mac.getMacLength()) {
|
|
||||||
throw new InvalidMessageException("Message shorter than crypto overhead!");
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(data);
|
|
||||||
verifyMac(inputStream, data.length, mac, null);
|
|
||||||
|
|
||||||
return new AttachmentCipherInputStream(new ByteArrayInputStream(data), parts[0], data.length - BLOCK_SIZE - mac.getMacLength());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidMacException e) {
|
|
||||||
throw new InvalidMessageException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AttachmentCipherInputStream(InputStream inputStream, byte[] cipherKey, long totalDataSize)
|
private AttachmentCipherInputStream(InputStream inputStream, byte[] cipherKey, long totalDataSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -1,123 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.crypto;
|
|
||||||
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.ByteUtil;
|
|
||||||
import org.session.libsignal.service.api.crypto.InvalidCiphertextException;
|
|
||||||
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
|
|
||||||
import org.session.libsignal.service.internal.util.Util;
|
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.spec.GCMParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
public class ProfileCipher {
|
|
||||||
|
|
||||||
public static final int NAME_PADDED_LENGTH = 26;
|
|
||||||
|
|
||||||
private final byte[] key;
|
|
||||||
|
|
||||||
public ProfileCipher(byte[] key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] encryptName(byte[] input, int paddedLength) {
|
|
||||||
try {
|
|
||||||
byte[] inputPadded = new byte[paddedLength];
|
|
||||||
|
|
||||||
if (input.length > inputPadded.length) {
|
|
||||||
throw new IllegalArgumentException("Input is too long: " + new String(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(input, 0, inputPadded, 0, input.length);
|
|
||||||
|
|
||||||
byte[] nonce = Util.getSecretBytes(12);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce));
|
|
||||||
|
|
||||||
return ByteUtil.combine(nonce, cipher.doFinal(inputPadded));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidAlgorithmParameterException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (BadPaddingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (IllegalBlockSizeException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] decryptName(byte[] input) throws InvalidCiphertextException {
|
|
||||||
try {
|
|
||||||
if (input.length < 12 + 16 + 1) {
|
|
||||||
throw new InvalidCiphertextException("Too short: " + input.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] nonce = new byte[12];
|
|
||||||
System.arraycopy(input, 0, nonce, 0, nonce.length);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, nonce));
|
|
||||||
|
|
||||||
byte[] paddedPlaintext = cipher.doFinal(input, nonce.length, input.length - nonce.length);
|
|
||||||
int plaintextLength = 0;
|
|
||||||
|
|
||||||
for (int i=paddedPlaintext.length-1;i>=0;i--) {
|
|
||||||
if (paddedPlaintext[i] != (byte)0x00) {
|
|
||||||
plaintextLength = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] plaintext = new byte[plaintextLength];
|
|
||||||
System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintextLength);
|
|
||||||
|
|
||||||
return plaintext;
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidAlgorithmParameterException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (IllegalBlockSizeException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new InvalidCiphertextException(e);
|
|
||||||
} catch (BadPaddingException e) {
|
|
||||||
throw new InvalidCiphertextException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verifyUnidentifiedAccess(byte[] theirUnidentifiedAccessVerifier) {
|
|
||||||
try {
|
|
||||||
if (theirUnidentifiedAccessVerifier == null || theirUnidentifiedAccessVerifier.length == 0) return false;
|
|
||||||
|
|
||||||
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(key);
|
|
||||||
|
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
|
||||||
mac.init(new SecretKeySpec(unidentifiedAccessKey, "HmacSHA256"));
|
|
||||||
|
|
||||||
byte[] ourUnidentifiedAccessVerifier = mac.doFinal(new byte[32]);
|
|
||||||
|
|
||||||
return MessageDigest.isEqual(theirUnidentifiedAccessVerifier, ourUnidentifiedAccessVerifier);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -19,10 +19,6 @@ public class UntrustedIdentityException extends Exception {
|
|||||||
this.identityKey = identityKey;
|
this.identityKey = identityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UntrustedIdentityException(UntrustedIdentityException e) {
|
|
||||||
this(e.getMessage(), e.getE164Number(), e.getIdentityKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdentityKey getIdentityKey() {
|
public IdentityKey getIdentityKey() {
|
||||||
return identityKey;
|
return identityKey;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.push.exceptions;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class UnregisteredUserException extends IOException {
|
|
||||||
|
|
||||||
private final String e164number;
|
|
||||||
|
|
||||||
public UnregisteredUserException(String e164number, Exception exception) {
|
|
||||||
super(exception);
|
|
||||||
this.e164number = e164number;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getE164Number() {
|
|
||||||
return e164number;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package org.session.libsignal.service.loki.api.shelved.p2p
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.timer
|
|
||||||
|
|
||||||
class LokiP2PAPI private constructor(private val userHexEncodedPublicKey: String, private val onPeerConnectionStatusChanged: (Boolean, String) -> Void, private val delegate: LokiP2PAPIDelegate) {
|
|
||||||
internal val peerInfo = mutableMapOf<String, PeerInfo>()
|
|
||||||
private val pingIntervals = mutableMapOf<String, Int>()
|
|
||||||
private val timers = mutableMapOf<String, Timer>()
|
|
||||||
|
|
||||||
// region Settings
|
|
||||||
/**
|
|
||||||
* The pinging interval for offline users.
|
|
||||||
*/
|
|
||||||
private val offlinePingInterval = 2 * 60 * 1000
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Types
|
|
||||||
internal data class PeerInfo(val contactHexEncodedPublicKey: String, val address: String, val port: Int, val isOnline: Boolean)
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Initialization
|
|
||||||
companion object {
|
|
||||||
private var isConfigured = false
|
|
||||||
|
|
||||||
lateinit var shared: LokiP2PAPI
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Must be called before `LokiAPI` is used.
|
|
||||||
*/
|
|
||||||
fun configure(userHexEncodedPublicKey: String, onPeerConnectionStatusChanged: (Boolean, String) -> Void, delegate: LokiP2PAPIDelegate) {
|
|
||||||
if (isConfigured) { return }
|
|
||||||
shared = LokiP2PAPI(userHexEncodedPublicKey, onPeerConnectionStatusChanged, delegate)
|
|
||||||
isConfigured = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Public API
|
|
||||||
fun handlePeerInfoReceived(contactHexEncodedPublicKey: String, address: String, port: Int, isP2PMessage: Boolean) {
|
|
||||||
// Avoid peers pinging eachother at the same time by staggering their timers
|
|
||||||
val pingInterval = if (contactHexEncodedPublicKey < this.userHexEncodedPublicKey) 1 * 60 else 2 * 60
|
|
||||||
pingIntervals[contactHexEncodedPublicKey] = pingInterval
|
|
||||||
val oldPeerInfo = peerInfo[contactHexEncodedPublicKey]
|
|
||||||
val newPeerInfo = PeerInfo(contactHexEncodedPublicKey, address, port, false)
|
|
||||||
peerInfo[contactHexEncodedPublicKey] = newPeerInfo
|
|
||||||
// Ping the peer back and mark them online based on the result of that call if either:
|
|
||||||
// • We didn't know about the peer at all, i.e. no P2P connection was established yet during this session
|
|
||||||
// • The message wasn't a P2P message, i.e. no P2P connection was established yet during this session or it was dropped for some reason
|
|
||||||
// • The peer was marked offline before; test the new P2P connection
|
|
||||||
// • The peer's address and/or port changed; test the new P2P connection
|
|
||||||
if (oldPeerInfo == null || !isP2PMessage || !oldPeerInfo.isOnline || oldPeerInfo.address != address || oldPeerInfo.port != port) {
|
|
||||||
delegate.ping(contactHexEncodedPublicKey)
|
|
||||||
} else {
|
|
||||||
mark(true, contactHexEncodedPublicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mark(isOnline: Boolean, contactHexEncodedPublicKey: String) {
|
|
||||||
val oldTimer = timers[contactHexEncodedPublicKey]
|
|
||||||
oldTimer?.cancel()
|
|
||||||
val pingInterval = if (isOnline) { pingIntervals[contactHexEncodedPublicKey]!! } else { offlinePingInterval }
|
|
||||||
val newTimer = timer(period = pingInterval.toLong()) { delegate.ping(contactHexEncodedPublicKey) }
|
|
||||||
timers[contactHexEncodedPublicKey] = newTimer
|
|
||||||
val updatedPeerInfo = peerInfo[contactHexEncodedPublicKey]!!.copy(isOnline = isOnline)
|
|
||||||
peerInfo[contactHexEncodedPublicKey] = updatedPeerInfo
|
|
||||||
onPeerConnectionStatusChanged(isOnline, contactHexEncodedPublicKey)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package org.session.libsignal.service.loki.api.shelved.p2p
|
|
||||||
|
|
||||||
interface LokiP2PAPIDelegate {
|
|
||||||
|
|
||||||
fun ping(contactHexEncodedPublicKey: String)
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user