Source added
This commit is contained in:
parent
b2864b500e
commit
ba28ca859e
8352 changed files with 1487182 additions and 1 deletions
1
microbenchmark/.gitignore
vendored
Normal file
1
microbenchmark/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
36
microbenchmark/benchmark-proguard-rules.pro
Normal file
36
microbenchmark/benchmark-proguard-rules.pro
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
-ignorewarnings
|
||||
|
||||
-keepattributes *Annotation*
|
||||
|
||||
-dontnote junit.framework.**
|
||||
-dontnote junit.runner.**
|
||||
|
||||
-dontwarn androidx.test.**
|
||||
-dontwarn org.junit.**
|
||||
-dontwarn com.squareup.javawriter.JavaWriter
|
||||
|
||||
-keepclasseswithmembers @org.junit.runner.RunWith public class *
|
||||
64
microbenchmark/build.gradle.kts
Normal file
64
microbenchmark/build.gradle.kts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("androidx.benchmark")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("ktlint")
|
||||
}
|
||||
|
||||
val signalBuildToolsVersion: String by rootProject.extra
|
||||
val signalCompileSdkVersion: String by rootProject.extra
|
||||
val signalTargetSdkVersion: Int by rootProject.extra
|
||||
val signalMinSdkVersion: Int by rootProject.extra
|
||||
val signalJavaVersion: JavaVersion by rootProject.extra
|
||||
val signalKotlinJvmTarget: String by rootProject.extra
|
||||
|
||||
android {
|
||||
namespace = "org.signal.microbenchmark"
|
||||
compileSdkVersion = signalCompileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = signalJavaVersion
|
||||
targetCompatibility = signalJavaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = signalKotlinJvmTarget
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = signalMinSdkVersion
|
||||
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
|
||||
}
|
||||
|
||||
testBuildType = "release"
|
||||
buildTypes {
|
||||
debug {
|
||||
// Since isDebuggable can't be modified by gradle for library modules,
|
||||
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro")
|
||||
}
|
||||
release {
|
||||
isDefault = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.android.tools.desugar)
|
||||
lintChecks(project(":lintchecks"))
|
||||
|
||||
implementation(project(":core-util"))
|
||||
|
||||
// Base dependencies
|
||||
androidTestImplementation(testLibs.junit.junit)
|
||||
androidTestImplementation(benchmarkLibs.androidx.test.ext.junit)
|
||||
androidTestImplementation(benchmarkLibs.androidx.benchmark.micro)
|
||||
|
||||
// Dependencies of modules being tested
|
||||
androidTestImplementation(project(":libsignal-service"))
|
||||
androidTestImplementation(libs.libsignal.android)
|
||||
}
|
||||
15
microbenchmark/src/androidTest/AndroidManifest.xml
Normal file
15
microbenchmark/src/androidTest/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!--
|
||||
Important: disable debugging for accurate performance results
|
||||
|
||||
In a com.android.library project, this flag must be disabled from this
|
||||
manifest, as it is not possible to override this flag from Gradle.
|
||||
-->
|
||||
<application
|
||||
android:debuggable="false"
|
||||
tools:ignore="HardcodedDebugMode"
|
||||
tools:replace="android:debuggable" />
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package org.signal.microbenchmark
|
||||
|
||||
import android.util.Log
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.signal.libsignal.protocol.logging.SignalProtocolLogger
|
||||
import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider
|
||||
import org.signal.util.SignalClient
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Benchmarks for decrypting messages.
|
||||
*
|
||||
* Note that in order to isolate all costs to just the process of decryption itself,
|
||||
* all operations are performed in in-memory stores.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ProtocolBenchmarks {
|
||||
|
||||
@get:Rule
|
||||
val benchmarkRule = BenchmarkRule()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
SignalProtocolLoggerProvider.setProvider { priority, tag, message ->
|
||||
when (priority) {
|
||||
SignalProtocolLogger.VERBOSE -> Log.v(tag, message)
|
||||
SignalProtocolLogger.DEBUG -> Log.d(tag, message)
|
||||
SignalProtocolLogger.INFO -> Log.i(tag, message)
|
||||
SignalProtocolLogger.WARN -> Log.w(tag, message)
|
||||
SignalProtocolLogger.ERROR -> Log.w(tag, message)
|
||||
SignalProtocolLogger.ASSERT -> Log.e(tag, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decrypt_unsealedSender() {
|
||||
val (alice, bob) = buildAndInitializeClients()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
val envelope = runWithTimingDisabled {
|
||||
alice.encryptUnsealedSender(bob)
|
||||
}
|
||||
|
||||
bob.decryptMessage(envelope)
|
||||
|
||||
// Respond so that the session ratchets
|
||||
runWithTimingDisabled {
|
||||
alice.decryptMessage(bob.encryptUnsealedSender(alice))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decrypt_sealedSender() {
|
||||
val (alice, bob) = buildAndInitializeClients()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
val envelope = runWithTimingDisabled {
|
||||
alice.encryptSealedSender(bob)
|
||||
}
|
||||
|
||||
bob.decryptMessage(envelope)
|
||||
|
||||
// Respond so that the session ratchets
|
||||
runWithTimingDisabled {
|
||||
alice.decryptMessage(bob.encryptSealedSender(alice))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multi_encrypt_sealedSender() {
|
||||
val recipientCount = 10
|
||||
val clients = buildAndInitializeClients(recipientCount)
|
||||
val alice = clients.first()
|
||||
val others = clients.filterNot { it == alice }
|
||||
val distributionId = DistributionId.create()
|
||||
|
||||
clients.forEach {
|
||||
it.initializedGroupSession(distributionId)
|
||||
}
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
alice.multiEncryptSealedSender(distributionId, others, Optional.empty())
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAndInitializeClients(): Pair<SignalClient, SignalClient> {
|
||||
val clients = buildAndInitializeClients(2)
|
||||
return clients[0] to clients[1]
|
||||
}
|
||||
|
||||
private fun buildAndInitializeClients(recipientCount: Int): List<SignalClient> {
|
||||
val clients = ArrayList<SignalClient>(recipientCount)
|
||||
for (n in 1..recipientCount) {
|
||||
clients.add(SignalClient())
|
||||
}
|
||||
|
||||
clients.forEach { alice ->
|
||||
clients.filterNot { it == alice }.forEach { bob ->
|
||||
alice.initializeSession(bob)
|
||||
bob.initializeSession(alice)
|
||||
|
||||
alice.decryptMessage(bob.encryptUnsealedSender(alice))
|
||||
|
||||
bob.decryptMessage(alice.encryptUnsealedSender(bob))
|
||||
|
||||
alice.decryptMessage(bob.encryptSealedSender(alice))
|
||||
|
||||
bob.decryptMessage(alice.encryptSealedSender(bob))
|
||||
}
|
||||
}
|
||||
|
||||
return clients
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
package org.signal.util
|
||||
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.signal.libsignal.protocol.groups.state.SenderKeyRecord
|
||||
import org.signal.libsignal.protocol.state.IdentityKeyStore
|
||||
import org.signal.libsignal.protocol.state.IdentityKeyStore.IdentityChange
|
||||
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.SessionRecord
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* An in-memory datastore specifically designed for tests.
|
||||
*/
|
||||
class InMemorySignalServiceAccountDataStore : SignalServiceAccountDataStore {
|
||||
|
||||
private val identityKey: IdentityKeyPair = IdentityKeyPair.generate()
|
||||
private val identities: MutableMap<SignalProtocolAddress, IdentityKey> = mutableMapOf()
|
||||
private val oneTimeEcPreKeys: MutableMap<Int, PreKeyRecord> = mutableMapOf()
|
||||
private val signedPreKeys: MutableMap<Int, SignedPreKeyRecord> = mutableMapOf()
|
||||
private var sessions: MutableMap<SignalProtocolAddress, SessionRecord> = mutableMapOf()
|
||||
private val senderKeys: MutableMap<SenderKeyLocator, SenderKeyRecord> = mutableMapOf()
|
||||
private val kyberPreKeys: MutableMap<Int, KyberPreKeyRecord> = mutableMapOf()
|
||||
|
||||
override fun getIdentityKeyPair(): IdentityKeyPair {
|
||||
return identityKey
|
||||
}
|
||||
|
||||
override fun getLocalRegistrationId(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun saveIdentity(address: SignalProtocolAddress, identityKey: IdentityKey): IdentityChange {
|
||||
val previous = identities.put(address, identityKey)
|
||||
return if (previous == null || previous == identityKey) {
|
||||
IdentityChange.NEW_OR_UNCHANGED
|
||||
} else {
|
||||
IdentityChange.REPLACED_EXISTING
|
||||
}
|
||||
}
|
||||
|
||||
override fun isTrustedIdentity(address: SignalProtocolAddress?, identityKey: IdentityKey?, direction: IdentityKeyStore.Direction?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIdentity(address: SignalProtocolAddress): IdentityKey? {
|
||||
return identities[address]
|
||||
}
|
||||
|
||||
override fun loadPreKey(preKeyId: Int): PreKeyRecord {
|
||||
return oneTimeEcPreKeys[preKeyId]!!
|
||||
}
|
||||
|
||||
override fun storePreKey(preKeyId: Int, record: PreKeyRecord) {
|
||||
oneTimeEcPreKeys[preKeyId] = record
|
||||
}
|
||||
|
||||
override fun containsPreKey(preKeyId: Int): Boolean {
|
||||
return oneTimeEcPreKeys.containsKey(preKeyId)
|
||||
}
|
||||
|
||||
override fun removePreKey(preKeyId: Int) {
|
||||
oneTimeEcPreKeys.remove(preKeyId)
|
||||
}
|
||||
|
||||
override fun loadSession(address: SignalProtocolAddress): SessionRecord {
|
||||
return sessions.getOrPut(address) { SessionRecord() }
|
||||
}
|
||||
|
||||
override fun loadExistingSessions(addresses: List<SignalProtocolAddress>): List<SessionRecord> {
|
||||
return addresses.map { sessions[it]!! }
|
||||
}
|
||||
|
||||
override fun getSubDeviceSessions(name: String): List<Int> {
|
||||
return sessions
|
||||
.filter { it.key.name == name && it.key.deviceId != 1 && it.value.isValid() }
|
||||
.map { it.key.deviceId }
|
||||
}
|
||||
|
||||
override fun storeSession(address: SignalProtocolAddress, record: SessionRecord) {
|
||||
sessions[address] = record
|
||||
}
|
||||
|
||||
override fun containsSession(address: SignalProtocolAddress): Boolean {
|
||||
return sessions[address]?.isValid() ?: false
|
||||
}
|
||||
|
||||
override fun deleteSession(address: SignalProtocolAddress) {
|
||||
sessions -= address
|
||||
}
|
||||
|
||||
override fun deleteAllSessions(name: String) {
|
||||
sessions = sessions.filter { it.key.name == name }.toMutableMap()
|
||||
}
|
||||
|
||||
override fun loadSignedPreKey(signedPreKeyId: Int): SignedPreKeyRecord {
|
||||
return signedPreKeys[signedPreKeyId]!!
|
||||
}
|
||||
|
||||
override fun loadSignedPreKeys(): List<SignedPreKeyRecord> {
|
||||
return signedPreKeys.values.toList()
|
||||
}
|
||||
|
||||
override fun storeSignedPreKey(signedPreKeyId: Int, record: SignedPreKeyRecord) {
|
||||
signedPreKeys[signedPreKeyId] = record
|
||||
}
|
||||
|
||||
override fun containsSignedPreKey(signedPreKeyId: Int): Boolean {
|
||||
return signedPreKeys.containsKey(signedPreKeyId)
|
||||
}
|
||||
|
||||
override fun removeSignedPreKey(signedPreKeyId: Int) {
|
||||
signedPreKeys -= signedPreKeyId
|
||||
}
|
||||
|
||||
override fun storeSenderKey(sender: SignalProtocolAddress, distributionId: UUID, record: SenderKeyRecord) {
|
||||
senderKeys[SenderKeyLocator(sender, distributionId)] = record
|
||||
}
|
||||
|
||||
override fun loadSenderKey(sender: SignalProtocolAddress, distributionId: UUID): SenderKeyRecord? {
|
||||
return senderKeys[SenderKeyLocator(sender, distributionId)]
|
||||
}
|
||||
|
||||
override fun loadKyberPreKey(kyberPreKeyId: Int): KyberPreKeyRecord {
|
||||
return kyberPreKeys[kyberPreKeyId]!!
|
||||
}
|
||||
|
||||
override fun loadKyberPreKeys(): List<KyberPreKeyRecord> {
|
||||
return kyberPreKeys.values.toList()
|
||||
}
|
||||
|
||||
override fun storeKyberPreKey(kyberPreKeyId: Int, record: KyberPreKeyRecord?) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun containsKyberPreKey(kyberPreKeyId: Int): Boolean {
|
||||
return kyberPreKeys.containsKey(kyberPreKeyId)
|
||||
}
|
||||
|
||||
override fun markKyberPreKeyUsed(kyberPreKeyId: Int, signedPreKeyId: Int, baseKey: ECPublicKey) {
|
||||
kyberPreKeys.remove(kyberPreKeyId)
|
||||
}
|
||||
|
||||
override fun deleteAllStaleOneTimeEcPreKeys(threshold: Long, minCount: Int) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun markAllOneTimeEcPreKeysStaleIfNecessary(staleTime: Long) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun storeLastResortKyberPreKey(kyberPreKeyId: Int, kyberPreKeyRecord: KyberPreKeyRecord) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun removeKyberPreKey(kyberPreKeyId: Int) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun markAllOneTimeKyberPreKeysStaleIfNecessary(staleTime: Long) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun deleteAllStaleOneTimeKyberPreKeys(threshold: Long, minCount: Int) {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun loadLastResortKyberPreKeys(): List<KyberPreKeyRecord> {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun archiveSession(address: SignalProtocolAddress) {
|
||||
sessions[address]!!.archiveCurrentState()
|
||||
}
|
||||
|
||||
override fun getAllAddressesWithActiveSessions(addressNames: MutableList<String>): MutableMap<SignalProtocolAddress, SessionRecord> {
|
||||
return sessions
|
||||
.filter { it.key.name in addressNames }
|
||||
.filter { it.value.isValid() }
|
||||
.toMutableMap()
|
||||
}
|
||||
|
||||
override fun getSenderKeySharedWith(distributionId: DistributionId): Set<SignalProtocolAddress> {
|
||||
error("Not used")
|
||||
}
|
||||
|
||||
override fun markSenderKeySharedWith(distributionId: DistributionId, addresses: Collection<SignalProtocolAddress>) {
|
||||
// Called, but not needed
|
||||
}
|
||||
|
||||
override fun clearSenderKeySharedWith(addresses: Collection<SignalProtocolAddress>) {
|
||||
// Called, but not needed
|
||||
}
|
||||
|
||||
override fun isMultiDevice(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun SessionRecord.isValid(): Boolean {
|
||||
return this.hasSenderChain()
|
||||
}
|
||||
|
||||
private data class SenderKeyLocator(val address: SignalProtocolAddress, val distributionId: UUID)
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
package org.signal.util
|
||||
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.libsignal.metadata.certificate.CertificateValidator
|
||||
import org.signal.libsignal.metadata.certificate.SenderCertificate
|
||||
import org.signal.libsignal.metadata.certificate.ServerCertificate
|
||||
import org.signal.libsignal.protocol.SessionBuilder
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair
|
||||
import org.signal.libsignal.protocol.ecc.ECPublicKey
|
||||
import org.signal.libsignal.protocol.groups.GroupSessionBuilder
|
||||
import org.signal.libsignal.protocol.kem.KEMKeyPair
|
||||
import org.signal.libsignal.protocol.kem.KEMKeyType
|
||||
import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage
|
||||
import org.signal.libsignal.protocol.state.PreKeyBundle
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeContent
|
||||
import org.whispersystems.signalservice.api.crypto.SealedSenderAccess
|
||||
import org.whispersystems.signalservice.api.crypto.SignalGroupSessionBuilder
|
||||
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.api.util.toByteArray
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
|
||||
import org.whispersystems.signalservice.internal.util.Util
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* An in-memory signal client that can encrypt and decrypt messages.
|
||||
*
|
||||
* Has a single prekey bundle that can be used to initialize a session with another client.
|
||||
*/
|
||||
class SignalClient {
|
||||
companion object {
|
||||
private val trustRoot: ECKeyPair = ECKeyPair.generate()
|
||||
}
|
||||
|
||||
private val lock = TestSessionLock()
|
||||
|
||||
private val aci: ACI = ACI.from(UUID.randomUUID())
|
||||
|
||||
private val store: SignalServiceAccountDataStore = InMemorySignalServiceAccountDataStore()
|
||||
|
||||
private var prekeyIndex = 0
|
||||
|
||||
private val unidentifiedAccessKey: ByteArray = Util.getSecretBytes(32)
|
||||
|
||||
private val senderCertificate: SenderCertificate = createCertificateFor(
|
||||
trustRoot = trustRoot,
|
||||
uuid = aci.rawUuid,
|
||||
e164 = "+${Random.nextLong(1111111111L, 9999999999L)}",
|
||||
deviceId = 1,
|
||||
identityKey = store.identityKeyPair.publicKey.publicKey,
|
||||
expires = Long.MAX_VALUE
|
||||
)
|
||||
|
||||
private val cipher = SignalServiceCipher(SignalServiceAddress(aci), 1, store, lock, CertificateValidator(trustRoot.publicKey))
|
||||
|
||||
/**
|
||||
* Sets up sessions using the [to] client's [preKeyBundles]. Note that you can only initialize a client up to 1,000 times because that's how many prekeys we have.
|
||||
*/
|
||||
fun initializeSession(to: SignalClient) {
|
||||
val address = SignalProtocolAddress(to.aci.toString(), 1)
|
||||
SessionBuilder(store, address).process(to.createPreKeyBundle())
|
||||
}
|
||||
|
||||
fun initializedGroupSession(distributionId: DistributionId): SenderKeyDistributionMessage {
|
||||
val self = SignalProtocolAddress(aci.toString(), 1)
|
||||
return SignalGroupSessionBuilder(lock, GroupSessionBuilder(store)).create(self, distributionId.asUuid())
|
||||
}
|
||||
|
||||
fun encryptUnsealedSender(to: SignalClient): Envelope {
|
||||
val sentTimestamp = System.currentTimeMillis()
|
||||
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
body = "Test Message",
|
||||
timestamp = sentTimestamp
|
||||
)
|
||||
)
|
||||
|
||||
val outgoingPushMessage: OutgoingPushMessage = cipher.encrypt(
|
||||
SignalProtocolAddress(to.aci.toString(), 1),
|
||||
SealedSenderAccess.NONE,
|
||||
EnvelopeContent.encrypted(content, ContentHint.RESENDABLE, Optional.empty())
|
||||
)
|
||||
|
||||
val encryptedContent: ByteArray = Base64.decode(outgoingPushMessage.content)
|
||||
val serviceGuid = UUID.randomUUID()
|
||||
|
||||
return Envelope(
|
||||
sourceServiceId = aci.toString(),
|
||||
sourceDevice = 1,
|
||||
destinationServiceId = to.aci.toString(),
|
||||
timestamp = sentTimestamp,
|
||||
serverTimestamp = sentTimestamp,
|
||||
serverGuid = serviceGuid.toString(),
|
||||
type = Envelope.Type.fromValue(outgoingPushMessage.type),
|
||||
urgent = true,
|
||||
content = encryptedContent.toByteString(),
|
||||
sourceServiceIdBinary = aci.toByteString(),
|
||||
destinationServiceIdBinary = to.aci.toByteString(),
|
||||
serverGuidBinary = serviceGuid.toByteArray().toByteString()
|
||||
)
|
||||
}
|
||||
|
||||
fun encryptSealedSender(to: SignalClient): Envelope {
|
||||
val sentTimestamp = System.currentTimeMillis()
|
||||
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
body = "Test Message",
|
||||
timestamp = sentTimestamp
|
||||
)
|
||||
)
|
||||
|
||||
val outgoingPushMessage: OutgoingPushMessage = cipher.encrypt(
|
||||
SignalProtocolAddress(to.aci.toString(), 1),
|
||||
SealedSenderAccess.forIndividual(UnidentifiedAccess(to.unidentifiedAccessKey, senderCertificate.serialized, false)),
|
||||
EnvelopeContent.encrypted(content, ContentHint.RESENDABLE, Optional.empty())
|
||||
)
|
||||
|
||||
val encryptedContent: ByteArray = Base64.decode(outgoingPushMessage.content)
|
||||
val serverGuid = UUID.randomUUID()
|
||||
|
||||
return Envelope(
|
||||
sourceServiceId = aci.toString(),
|
||||
sourceDevice = 1,
|
||||
destinationServiceId = to.aci.toString(),
|
||||
timestamp = sentTimestamp,
|
||||
serverTimestamp = sentTimestamp,
|
||||
serverGuid = serverGuid.toString(),
|
||||
type = Envelope.Type.fromValue(outgoingPushMessage.type),
|
||||
urgent = true,
|
||||
content = encryptedContent.toByteString(),
|
||||
sourceServiceIdBinary = aci.toByteString(),
|
||||
destinationServiceIdBinary = to.aci.toByteString(),
|
||||
serverGuidBinary = serverGuid.toByteArray().toByteString()
|
||||
)
|
||||
}
|
||||
|
||||
fun multiEncryptSealedSender(distributionId: DistributionId, others: List<SignalClient>, groupId: Optional<ByteArray>): ByteArray {
|
||||
val sentTimestamp = System.currentTimeMillis()
|
||||
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
body = "Test Message",
|
||||
timestamp = sentTimestamp
|
||||
)
|
||||
)
|
||||
val destinations = others.map { bob ->
|
||||
SignalProtocolAddress(bob.aci.toString(), 1)
|
||||
}
|
||||
|
||||
return cipher.encryptForGroup(distributionId, destinations, null, senderCertificate, content.encode(), ContentHint.DEFAULT, groupId)
|
||||
}
|
||||
|
||||
fun decryptMessage(envelope: Envelope) {
|
||||
cipher.decrypt(envelope, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
private fun createPreKeyBundle(): PreKeyBundle {
|
||||
val prekeyId = prekeyIndex++
|
||||
val preKeyRecord = PreKeyRecord(prekeyId, ECKeyPair.generate())
|
||||
val signedPreKeyPair = ECKeyPair.generate()
|
||||
val signedPreKeySignature = store.identityKeyPair.privateKey.calculateSignature(signedPreKeyPair.publicKey.serialize())
|
||||
val kyerPair = KEMKeyPair.generate(KEMKeyType.KYBER_1024)
|
||||
|
||||
store.storePreKey(prekeyId, preKeyRecord)
|
||||
store.storeSignedPreKey(prekeyId, SignedPreKeyRecord(prekeyId, System.currentTimeMillis(), signedPreKeyPair, signedPreKeySignature))
|
||||
|
||||
return PreKeyBundle(
|
||||
prekeyId, prekeyId, prekeyId, preKeyRecord.keyPair.publicKey, prekeyId, signedPreKeyPair.publicKey, signedPreKeySignature, store.identityKeyPair.publicKey,
|
||||
PreKeyBundle.NULL_PRE_KEY_ID, kyerPair.publicKey, kyerPair.secretKey.serialize()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCertificateFor(trustRoot: ECKeyPair, uuid: UUID, e164: String, deviceId: Int, identityKey: ECPublicKey, expires: Long): SenderCertificate {
|
||||
val serverKey: ECKeyPair = ECKeyPair.generate()
|
||||
val serverCertificate = ServerCertificate(trustRoot.privateKey, 1, serverKey.publicKey)
|
||||
return serverCertificate.issue(serverKey.privateKey, uuid.toString(), Optional.of(e164), deviceId, identityKey, expires)
|
||||
}
|
||||
|
||||
private class TestSessionLock : SignalSessionLock {
|
||||
val lock = ReentrantLock()
|
||||
|
||||
override fun acquire(): SignalSessionLock.Lock {
|
||||
lock.lock()
|
||||
return SignalSessionLock.Lock { lock.unlock() }
|
||||
}
|
||||
}
|
||||
2
microbenchmark/src/main/AndroidManifest.xml
Normal file
2
microbenchmark/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
||||
Loading…
Add table
Add a link
Reference in a new issue