Add shared SecureRandom instance

This commit is contained in:
bemusementpark 2024-08-04 00:18:47 +09:30
parent a56e1d0b91
commit f5d1debc32
26 changed files with 98 additions and 155 deletions

View File

@ -33,18 +33,16 @@ import android.view.View
import com.annimon.stream.Stream
import com.google.android.mms.pdu_alt.CharacterSets
import com.google.android.mms.pdu_alt.EncodedStringValue
import network.loki.messenger.R
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.components.ComposeText
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.security.SecureRandom
import java.util.Arrays
import java.util.Collections
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.math.min
import network.loki.messenger.R
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.components.ComposeText
object Util {
private val TAG: String = Log.tag(Util::class.java)
@ -248,32 +246,6 @@ object Util {
return result
}
fun getSecretBytes(size: Int): ByteArray {
return getSecretBytes(SecureRandom(), size)
}
fun getSecretBytes(secureRandom: SecureRandom, size: Int): ByteArray {
val secret = ByteArray(size)
secureRandom.nextBytes(secret)
return secret
}
fun <T> getRandomElement(elements: Array<T>): T {
return elements[SecureRandom().nextInt(elements.size)]
}
fun <T> getRandomElement(elements: List<T>): T {
return elements[SecureRandom().nextInt(elements.size)]
}
fun equals(a: Any?, b: Any?): Boolean {
return a === b || (a != null && a == b)
}
fun hashCode(vararg objects: Any?): Int {
return objects.contentHashCode()
}
fun uri(uri: String?): Uri? {
return if (uri == null) null
else Uri.parse(uri)

View File

@ -1,14 +1,14 @@
package org.thoughtcrime.securesms.crypto;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import org.session.libsession.utilities.TextSecurePreferences;
import java.security.SecureRandom;
/**
* A provider that is responsible for creating or retrieving the AttachmentSecret model.
*
@ -81,9 +81,8 @@ public class AttachmentSecretProvider {
}
private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context) {
SecureRandom random = new SecureRandom();
byte[] secret = new byte[32];
random.nextBytes(secret);
SECURE_RANDOM.nextBytes(secret);
AttachmentSecret attachmentSecret = new AttachmentSecret(null, null, secret);
storeAttachmentSecret(context, attachmentSecret);

View File

@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.crypto;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
@ -8,7 +10,6 @@ import androidx.annotation.NonNull;
import org.session.libsession.utilities.TextSecurePreferences;
import java.io.IOException;
import java.security.SecureRandom;
public class DatabaseSecretProvider {
@ -60,9 +61,8 @@ public class DatabaseSecretProvider {
}
private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) {
SecureRandom random = new SecureRandom();
byte[] secret = new byte[32];
random.nextBytes(secret);
SECURE_RANDOM.nextBytes(secret);
DatabaseSecret databaseSecret = new DatabaseSecret(secret);

View File

@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.crypto;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import androidx.annotation.NonNull;
import android.util.Pair;
@ -11,7 +13,6 @@ import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
@ -31,7 +32,7 @@ public class ModernEncryptingPartOutputStream {
throws IOException
{
byte[] random = new byte[32];
new SecureRandom().nextBytes(random);
SECURE_RANDOM.nextBytes(random);
try {
Mac mac = Mac.getInstance("HmacSHA256");

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.database;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
@ -26,7 +28,6 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.BitmapUtil;
import java.io.Closeable;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@ -303,7 +304,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
public void updateProfilePicture(String groupID, byte[] newValue) {
long avatarId;
if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong());
if (newValue != null) avatarId = Math.abs(SECURE_RANDOM.nextLong());
else avatarId = 0;
@ -458,12 +459,6 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId});
}
public byte[] allocateGroupId() {
byte[] groupId = new byte[16];
new SecureRandom().nextBytes(groupId);
return groupId;
}
public boolean hasGroup(@NonNull String groupId) {
try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery(
"SELECT 1 FROM " + TABLE_NAME + " WHERE " + GROUP_ID + " = ? LIMIT 1",

View File

@ -46,11 +46,11 @@ import org.session.libsession.utilities.IdentityKeyMismatchList
import org.session.libsession.utilities.NetworkFailure
import org.session.libsession.utilities.NetworkFailureList
import org.session.libsession.utilities.TextSecurePreferences.Companion.isReadReceiptsEnabled
import org.session.libsession.utilities.Util.toIsoBytes
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.ThreadUtils.queue
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment
import org.thoughtcrime.securesms.database.SmsDatabase.InsertListener
@ -66,7 +66,6 @@ import org.thoughtcrime.securesms.mms.SlideDeck
import org.thoughtcrime.securesms.util.asSequence
import java.io.Closeable
import java.io.IOException
import java.security.SecureRandom
import java.util.LinkedList
class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : MessagingDatabase(context, databaseHelper) {
@ -1200,7 +1199,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
inner class OutgoingMessageReader(private val message: OutgoingMediaMessage?,
private val threadId: Long) {
private val id = SecureRandom().nextLong()
private val id = SECURE_RANDOM.nextLong()
val current: MessageRecord
get() {
val slideDeck = SlideDeck(context, message!!.attachments)

View File

@ -17,6 +17,8 @@
*/
package org.thoughtcrime.securesms.database;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@ -49,7 +51,6 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
@ -784,7 +785,7 @@ public class SmsDatabase extends MessagingDatabase {
public OutgoingMessageReader(OutgoingTextMessage message, long threadId) {
this.message = message;
this.threadId = threadId;
this.id = new SecureRandom().nextLong();
this.id = SECURE_RANDOM.nextLong();
}
public MessageRecord getCurrent() {

View File

@ -1,9 +1,10 @@
package org.thoughtcrime.securesms.glide;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.security.SecureRandom;
import okhttp3.Headers;
import okhttp3.Interceptor;
@ -30,15 +31,15 @@ public class PaddedHeadersInterceptor implements Interceptor {
private @NonNull Headers getPaddedHeaders(@NonNull Headers headers) {
return headers.newBuilder()
.add(PADDING_HEADER, getRandomString(new SecureRandom(), MIN_RANDOM_BYTES, MAX_RANDOM_BYTES))
.add(PADDING_HEADER, getRandomString(MIN_RANDOM_BYTES, MAX_RANDOM_BYTES))
.build();
}
private static @NonNull String getRandomString(@NonNull SecureRandom secureRandom, int minLength, int maxLength) {
char[] buffer = new char[secureRandom.nextInt(maxLength - minLength) + minLength];
private static @NonNull String getRandomString(int minLength, int maxLength) {
char[] buffer = new char[SECURE_RANDOM.nextInt(maxLength - minLength) + minLength];
for (int i = 0 ; i < buffer.length; i++) {
buffer[i] = (char) (secureRandom.nextInt(74) + 48); // Random char from 0-Z
buffer[i] = (char) (SECURE_RANDOM.nextInt(74) + 48); // Random char from 0-Z
}
return new String(buffer);

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.logging;
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import androidx.annotation.NonNull;
@ -17,7 +18,6 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@ -64,7 +64,7 @@ class LogFile {
}
void writeEntry(@NonNull String entry) throws IOException {
new SecureRandom().nextBytes(ivBuffer);
SECURE_RANDOM.nextBytes(ivBuffer);
byte[] plaintext = entry.getBytes();
try {

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.logging;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
@ -9,7 +11,6 @@ import org.session.libsignal.utilities.Base64;
import org.session.libsession.utilities.TextSecurePreferences;
import java.io.IOException;
import java.security.SecureRandom;
class LogSecretProvider {
@ -40,9 +41,8 @@ class LogSecretProvider {
}
private static byte[] createAndStoreSecret(@NonNull Context context) {
SecureRandom random = new SecureRandom();
byte[] secret = new byte[32];
random.nextBytes(secret);
SECURE_RANDOM.nextBytes(secret);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(secret);

View File

@ -21,7 +21,6 @@ import android.content.res.Resources
import android.net.Uri
import androidx.annotation.DrawableRes
import com.squareup.phrase.Phrase
import java.security.SecureRandom
import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
@ -29,6 +28,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.UriAttachm
import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY
import org.session.libsession.utilities.Util.equals
import org.session.libsession.utilities.Util.hashCode
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.conversation.v2.Util
import org.thoughtcrime.securesms.util.MediaUtil
@ -160,7 +160,7 @@ abstract class Slide(@JvmField protected val context: Context, protected val att
): Attachment {
val resolvedType =
Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime)
val fastPreflightId = SecureRandom().nextLong().toString()
val fastPreflightId = SECURE_RANDOM.nextLong().toString()
return UriAttachment(
uri,
if (hasThumbnail) uri else null,

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.net;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import androidx.annotation.NonNull;
import android.text.TextUtils;
@ -15,7 +17,6 @@ import org.session.libsignal.utilities.guava.Optional;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -54,7 +55,7 @@ public class ChunkedDataFetcher {
private RequestController fetchChunksWithUnknownTotalSize(@NonNull String url, @NonNull Callback callback) {
CompositeRequestController compositeController = new CompositeRequestController();
long chunkSize = new SecureRandom().nextInt(1024) + 1024;
long chunkSize = SECURE_RANDOM.nextInt(1024) + 1024;
Request request = new Request.Builder()
.url(url)
.cacheControl(NO_CACHE)

View File

@ -1,9 +1,9 @@
package org.thoughtcrime.securesms.permissions;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
@ -11,9 +11,7 @@ import android.os.Build;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
@ -28,13 +26,10 @@ import org.session.libsession.utilities.ServiceUtil;
import org.thoughtcrime.securesms.util.LRUCache;
import java.lang.ref.WeakReference;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import network.loki.messenger.R;
public class Permissions {
private static final Map<Integer, PermissionsRequest> OUTSTANDING = new LRUCache<>(2);
@ -172,7 +167,7 @@ public class Permissions {
}
private void executePermissionsRequest(PermissionsRequest request) {
int requestCode = new SecureRandom().nextInt(65434) + 100;
int requestCode = SECURE_RANDOM.nextInt(65434) + 100;
synchronized (OUTSTANDING) {
OUTSTANDING.put(requestCode, request);

View File

@ -62,6 +62,7 @@ import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.truncateIdForDisplay
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.avatar.AvatarSelection
import org.thoughtcrime.securesms.components.ProfilePictureView
@ -90,7 +91,6 @@ import org.thoughtcrime.securesms.util.NetworkUtils
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show
import java.io.File
import java.security.SecureRandom
import javax.inject.Inject
@AndroidEntryPoint
@ -294,7 +294,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
val userConfig = configFactory.user
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
prefs.setProfileAvatarId(SecureRandom().nextInt() )
prefs.setProfileAvatarId(SECURE_RANDOM.nextInt() )
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
// Attempt to grab the details we require to update the profile picture

View File

@ -1,8 +1,10 @@
package org.thoughtcrime.securesms.webrtc
import android.content.Context
import org.session.libsignal.crypto.shuffledRandom
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.SettableFuture
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import org.thoughtcrime.securesms.webrtc.video.Camera
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
import org.thoughtcrime.securesms.webrtc.video.CameraState
@ -22,9 +24,7 @@ import org.webrtc.SurfaceTextureHelper
import org.webrtc.VideoSink
import org.webrtc.VideoSource
import org.webrtc.VideoTrack
import java.security.SecureRandom
import java.util.concurrent.ExecutionException
import kotlin.random.asKotlinRandom
class PeerConnectionWrapper(private val context: Context,
private val factory: PeerConnectionFactory,
@ -49,8 +49,7 @@ class PeerConnectionWrapper(private val context: Context,
private var isInitiator = false
private fun initPeerConnection() {
val random = SecureRandom().asKotlinRandom()
val iceServers = listOf("freyr","angus","hereford","holstein", "brahman").shuffled(random).take(2).map { sub ->
val iceServers = listOf("freyr","angus","hereford","holstein", "brahman").shuffledRandom().take(2).map { sub ->
PeerConnection.IceServer.builder("turn:$sub.getsession.org")
.setUsername("session202111")
.setPassword("053c268164bc7bd7")

View File

@ -12,11 +12,11 @@ import org.session.libsession.utilities.Util.equals
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.streams.ProfileCipherInputStream
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.security.SecureRandom
import java.util.concurrent.ConcurrentSkipListSet
class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipientAddress: Address): Job {
@ -64,7 +64,7 @@ class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipient
Log.w(TAG, "Removing profile avatar for: " + recipient.address.serialize())
if (recipient.isLocalNumber) {
setProfileAvatarId(context, SecureRandom().nextInt())
setProfileAvatarId(context, SECURE_RANDOM.nextInt())
setProfilePictureURL(context, null)
}
@ -83,7 +83,7 @@ class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipient
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.address))
if (recipient.isLocalNumber) {
setProfileAvatarId(context, SecureRandom().nextInt())
setProfileAvatarId(context, SECURE_RANDOM.nextInt())
setProfilePictureURL(context, profileAvatar)
}

View File

@ -10,7 +10,7 @@ import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageReceiveParameters
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.GroupUtil
import org.session.libsignal.crypto.getRandomElementOrNull
import org.session.libsignal.crypto.secureRandomOrNull
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Namespace
import org.session.libsignal.utilities.defaultRequiresAuth
@ -104,7 +104,7 @@ class ClosedGroupPollerV2 {
fun poll(groupPublicKey: String): Promise<Unit, Exception> {
if (!isPolling(groupPublicKey)) { return Promise.of(Unit) }
val promise = SnodeAPI.getSwarm(groupPublicKey).bind { swarm ->
val snode = swarm.getRandomElementOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure
val snode = swarm.secureRandomOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure
if (!isPolling(groupPublicKey)) { throw PollingCanceledException() }
val currentForkInfo = SnodeAPI.forkInfo
when {

View File

@ -19,8 +19,6 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.BatchMessageReceiveJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageReceiveParameters
import org.session.libsession.messaging.messages.control.SharedConfigurationMessage
import org.session.libsession.messaging.sending_receiving.MessageReceiver
import org.session.libsession.snode.RawResponse
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeModule
@ -29,7 +27,7 @@ import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Namespace
import org.session.libsignal.utilities.Snode
import java.security.SecureRandom
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import java.util.Timer
import java.util.TimerTask
import kotlin.time.Duration.Companion.days
@ -106,7 +104,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti
val swarm = SnodeModule.shared.storage.getSwarm(userPublicKey) ?: setOf()
val unusedSnodes = swarm.subtract(usedSnodes)
if (unusedSnodes.isNotEmpty()) {
val index = SecureRandom().nextInt(unusedSnodes.size)
val index = SECURE_RANDOM.nextInt(unusedSnodes.size)
val nextSnode = unusedSnodes.elementAt(index)
usedSnodes.add(nextSnode)
Log.d(TAG, "Polling $nextSnode.")

View File

@ -28,7 +28,7 @@ object MessageWrapper {
val webSocketMessage = createWebSocketMessage(envelope)
return webSocketMessage.toByteArray()
} catch (e: Exception) {
throw if (e is Error) { e } else { Error.FailedToWrapData }
throw if (e is Error) e else Error.FailedToWrapData
}
}
@ -49,15 +49,15 @@ object MessageWrapper {
private fun createWebSocketMessage(envelope: Envelope): WebSocketMessage {
try {
val requestBuilder = WebSocketRequestMessage.newBuilder()
requestBuilder.verb = "PUT"
requestBuilder.path = "/api/v1/message"
requestBuilder.id = SecureRandom.getInstance("SHA1PRNG").nextLong()
requestBuilder.body = envelope.toByteString()
val messageBuilder = WebSocketMessage.newBuilder()
messageBuilder.request = requestBuilder.build()
messageBuilder.type = WebSocketMessage.Type.REQUEST
return messageBuilder.build()
return WebSocketMessage.newBuilder().apply {
request = WebSocketRequestMessage.newBuilder().apply {
verb = "PUT"
path = "/api/v1/message"
id = SecureRandom.getInstance("SHA1PRNG").nextLong()
body = envelope.toByteString()
}.build()
type = WebSocketMessage.Type.REQUEST
}.build()
} catch (e: Exception) {
Log.d("Loki", "Failed to wrap envelope in web socket message: ${e.message}.")
throw Error.FailedToWrapEnvelopeInWebSocketMessage

View File

@ -10,11 +10,10 @@ import okhttp3.Request
import org.session.libsession.messaging.file_server.FileServerApi
import org.session.libsession.utilities.AESGCM
import org.session.libsession.utilities.AESGCM.EncryptionResult
import org.session.libsession.utilities.Util
import org.session.libsession.utilities.getBodyForOnionRequest
import org.session.libsession.utilities.getHeadersForOnionRequest
import org.session.libsignal.crypto.getRandomElement
import org.session.libsignal.crypto.getRandomElementOrNull
import org.session.libsignal.crypto.secureRandom
import org.session.libsignal.crypto.secureRandomOrNull
import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Broadcaster
@ -149,7 +148,7 @@ object OnionRequestAPI {
val reusableGuardSnodeCount = reusableGuardSnodes.count()
if (unusedSnodes.count() < (targetGuardSnodeCount - reusableGuardSnodeCount)) { throw InsufficientSnodesException() }
fun getGuardSnode(): Promise<Snode, Exception> {
val candidate = unusedSnodes.getRandomElementOrNull()
val candidate = unusedSnodes.secureRandomOrNull()
?: return Promise.ofFail(InsufficientSnodesException())
unusedSnodes = unusedSnodes.minus(candidate)
Log.d("Loki", "Testing guard snode: $candidate.")
@ -191,7 +190,7 @@ object OnionRequestAPI {
// Don't test path snodes as this would reveal the user's IP to them
guardSnodes.minus(reusableGuardSnodes).map { guardSnode ->
val result = listOf( guardSnode ) + (0 until (pathSize - 1)).mapIndexed() { index, _ ->
var pathSnode = unusedSnodes.getRandomElement()
var pathSnode = unusedSnodes.secureRandom()
// remove the snode from the unused list and return it
unusedSnodes = unusedSnodes.minus(pathSnode)
@ -228,9 +227,9 @@ object OnionRequestAPI {
OnionRequestAPI.guardSnodes = guardSnodes
fun getPath(paths: List<Path>): Path {
return if (snodeToExclude != null) {
paths.filter { !it.contains(snodeToExclude) }.getRandomElement()
paths.filter { !it.contains(snodeToExclude) }.secureRandom()
} else {
paths.getRandomElement()
paths.secureRandom()
}
}
when {
@ -273,7 +272,7 @@ object OnionRequestAPI {
path.removeAt(snodeIndex)
val unusedSnodes = SnodeAPI.snodePool.minus(oldPaths.flatten())
if (unusedSnodes.isEmpty()) { throw InsufficientSnodesException() }
path.add(unusedSnodes.getRandomElement())
path.add(unusedSnodes.secureRandom())
// Don't test the new snode as this would reveal the user's IP
oldPaths.removeAt(pathIndex)
val newPaths = oldPaths + listOf( path )

View File

@ -18,7 +18,8 @@ import nl.komponents.kovenant.task
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.utilities.MessageWrapper
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
import org.session.libsignal.crypto.getRandomElement
import org.session.libsignal.crypto.secureRandom
import org.session.libsignal.crypto.shuffledRandom
import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Base64
@ -30,6 +31,7 @@ import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Namespace
import org.session.libsignal.utilities.Snode
import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import org.session.libsignal.utilities.prettifiedDescription
import org.session.libsignal.utilities.retryIfNeeded
import java.security.SecureRandom
@ -209,7 +211,7 @@ object SnodeAPI {
Log.d("Loki", "Persisting snode pool to database.")
this.snodePool = snodePool
try {
deferred.resolve(snodePool.getRandomElement())
deferred.resolve(snodePool.secureRandom())
} catch (exception: Exception) {
Log.d("Loki", "Got an empty snode pool from: $target.")
deferred.reject(SnodeAPI.Error.Generic)
@ -224,7 +226,7 @@ object SnodeAPI {
}
return deferred.promise
} else {
return Promise.of(snodePool.getRandomElement())
return Promise.of(snodePool.secureRandom())
}
}
@ -241,8 +243,8 @@ object SnodeAPI {
}
internal fun getSingleTargetSnode(publicKey: String): Promise<Snode, Exception> {
// SecureRandom() should be cryptographically secure
return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() }
// SecureRandom should be cryptographically secure
return getSwarm(publicKey).map { it.shuffledRandom().random() }
}
// Public API

View File

@ -13,6 +13,7 @@ import android.text.TextUtils
import android.text.style.StyleSpan
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import java.io.*
import java.nio.charset.StandardCharsets
import java.security.SecureRandom
@ -292,15 +293,10 @@ object Util {
@JvmStatic
fun getSecretBytes(size: Int): ByteArray {
val secret = ByteArray(size)
getSecureRandom().nextBytes(secret)
SECURE_RANDOM.nextBytes(secret)
return secret
}
@JvmStatic
fun getSecureRandom(): SecureRandom {
return SecureRandom()
}
@JvmStatic
fun getFirstNonEmpty(vararg values: String?): String? {
for (value in values) {
@ -317,18 +313,14 @@ object Util {
}
@JvmStatic
fun <T> getRandomElement(elements: Array<T>): T {
return elements[SecureRandom().nextInt(elements.size)]
}
fun <T> getRandomElement(elements: Array<T>): T = elements[SECURE_RANDOM.nextInt(elements.size)]
@JvmStatic
fun getBoldedString(value: String?): CharSequence {
if (value.isNullOrEmpty()) { return "" }
val spanned = SpannableString(value)
spanned.setSpan(StyleSpan(Typeface.BOLD), 0,
spanned.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spanned
return SpannableString(value).also {
it.setSpan(StyleSpan(Typeface.BOLD), 0, it.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
@JvmStatic

View File

@ -1,19 +1,23 @@
package org.session.libsignal.crypto
import java.security.SecureRandom
import org.session.libsignal.utilities.Util.SECURE_RANDOM
/**
* Uses `SecureRandom` to pick an element from this collection.
*/
fun <T> Collection<T>.getRandomElementOrNull(): T? {
fun <T> Collection<T>.secureRandomOrNull(): T? {
if (isEmpty()) return null
val index = SecureRandom().nextInt(size) // SecureRandom() should be cryptographically secure
val index = SECURE_RANDOM.nextInt(size) // SecureRandom should be cryptographically secure
return elementAtOrNull(index)
}
/**
* Uses `SecureRandom` to pick an element from this collection.
*
* @throws [NullPointerException] if the [Collection] is empty
*/
fun <T> Collection<T>.getRandomElement(): T {
return getRandomElementOrNull()!!
fun <T> Collection<T>.secureRandom(): T {
return secureRandomOrNull()!!
}
fun <T> Collection<T>.shuffledRandom(): List<T> = shuffled(SECURE_RANDOM)

View File

@ -1,13 +1,13 @@
package org.session.libsignal.streams;
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
import static org.session.libsignal.utilities.Util.SECURE_RANDOM;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@ -80,7 +80,7 @@ public class ProfileCipherOutputStream extends DigestingOutputStream {
private byte[] generateNonce() {
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
SECURE_RANDOM.nextBytes(nonce);
return nonce;
}

View File

@ -5,8 +5,7 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import java.security.SecureRandom
import org.session.libsignal.utilities.Util.SECURE_RANDOM
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
@ -35,7 +34,7 @@ object HTTP {
override fun getAcceptedIssuers(): Array<X509Certificate> { return arrayOf() }
}
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf( trustManager ), SecureRandom())
sslContext.init(null, arrayOf( trustManager ), SECURE_RANDOM)
OkHttpClient().newBuilder()
.sslSocketFactory(sslContext.socketFactory, trustManager)
.hostnameVerifier { _, _ -> true }
@ -55,7 +54,7 @@ object HTTP {
override fun getAcceptedIssuers(): Array<X509Certificate> { return arrayOf() }
}
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf( trustManager ), SecureRandom())
sslContext.init(null, arrayOf( trustManager ), SECURE_RANDOM)
return OkHttpClient().newBuilder()
.sslSocketFactory(sslContext.socketFactory, trustManager)
.hostnameVerifier { _, _ -> true }

View File

@ -12,12 +12,10 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Util {
public static SecureRandom SECURE_RANDOM = new SecureRandom();
public static byte[] join(byte[]... input) {
try {
@ -67,7 +65,7 @@ public class Util {
}
public static boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
public static byte[] getSecretBytes(int size) {
@ -80,13 +78,6 @@ public class Util {
}
}
public static byte[] getRandomLengthBytes(int maxSize) {
SecureRandom secureRandom = new SecureRandom();
byte[] result = new byte[secureRandom.nextInt(maxSize) + 1];
secureRandom.nextBytes(result);
return result;
}
public static String readFully(InputStream in) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
@ -98,7 +89,7 @@ public class Util {
in.close();
return new String(bout.toByteArray());
return bout.toString();
}
public static void readFully(InputStream in, byte[] buffer) throws IOException {
@ -146,9 +137,4 @@ public class Util {
}
return (int)value;
}
public static <T> List<T> immutableList(T... elements) {
return Collections.unmodifiableList(Arrays.asList(elements.clone()));
}
}