mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-24 23:09:02 +03:00
Merge branch 'develop' into feature/ftue
This commit is contained in:
commit
c8f43e73e2
244 changed files with 3943 additions and 1379 deletions
.buildkite
.editorconfig.idea/codeStyles
CHANGES.mdCONTRIBUTING.mddocs
matrix-sdk-android/src
androidTest/java/im/vector/matrix/android
common
internal/crypto
AttachmentEncryptionTest.ktCryptoStoreTest.ktExportEncryptionTest.kt
crosssigning
keysbackup
ssss
verification
main/java/im/vector/matrix/android
api
crypto
extensions
session
internal
auth
crypto
CryptoConstants.ktIncomingRoomKeyRequestManager.ktMXOlmDevice.kt
api
crosssigning
keysbackup/api
model
secrets
store
tasks
tools
verification
DefaultIncomingSASDefaultVerificationTransaction.ktDefaultOutgoingSASDefaultVerificationTransaction.ktDefaultVerificationService.ktDefaultVerificationTransaction.ktPendingVerificationRequest.ktSASDefaultVerificationTransaction.ktVerificationEmoji.ktVerificationTransport.ktVerificationTransportRoomMessage.ktVerificationTransportToDevice.kt
qrcode
database
extensions
network
session
DefaultSession.kt
content
homeserver
pushers
room
securestorage
signout
|
@ -81,7 +81,7 @@ steps:
|
||||||
|
|
||||||
- label: "ktlint"
|
- label: "ktlint"
|
||||||
command:
|
command:
|
||||||
- "curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint"
|
- "curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.36.0/ktlint && chmod a+x ktlint"
|
||||||
- "./ktlint --android --experimental -v"
|
- "./ktlint --android --experimental -v"
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.1.0:
|
- docker#v3.1.0:
|
||||||
|
|
|
@ -12,7 +12,7 @@ max_line_length=off
|
||||||
# Comma-separated list of rules to disable (Since 0.34.0)
|
# Comma-separated list of rules to disable (Since 0.34.0)
|
||||||
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
|
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
|
||||||
# by the ruleset identifier.
|
# by the ruleset identifier.
|
||||||
disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
|
disabled_rules=no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation
|
||||||
|
|
||||||
# The following (so far identified) rules are kept:
|
# The following (so far identified) rules are kept:
|
||||||
# no-blank-line-before-rbrace
|
# no-blank-line-before-rbrace
|
||||||
|
@ -30,3 +30,4 @@ disabled_rules=no-wildcard-imports,no-multi-spaces,colon-spacing,chain-wrapping,
|
||||||
# no-empty-class-body
|
# no-empty-class-body
|
||||||
# experimental:multiline-if-else
|
# experimental:multiline-if-else
|
||||||
# experimental:no-empty-first-line-in-method-block
|
# experimental:no-empty-first-line-in-method-block
|
||||||
|
# no-wildcard-imports
|
||||||
|
|
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
|
@ -1,6 +1,9 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="RIGHT_MARGIN" value="160" />
|
<option name="RIGHT_MARGIN" value="160" />
|
||||||
|
<AndroidXmlCodeStyleSettings>
|
||||||
|
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||||
|
</AndroidXmlCodeStyleSettings>
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
<value>
|
<value>
|
||||||
|
|
41
CHANGES.md
41
CHANGES.md
|
@ -1,4 +1,30 @@
|
||||||
Changes in RiotX 0.17.0 (2020-XX-XX)
|
Changes in RiotX 0.18.0 (2020-XX-XX)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features ✨:
|
||||||
|
-
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Add support for `/plain` command (#12)
|
||||||
|
- FTUE: do not display a different color when encrypting message when not in developer mode.
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Fix crash on attachment preview screen (#1088)
|
||||||
|
|
||||||
|
Translations 🗣:
|
||||||
|
-
|
||||||
|
|
||||||
|
SDK API changes ⚠️:
|
||||||
|
-
|
||||||
|
|
||||||
|
Build 🧱:
|
||||||
|
- Upgrade ktlint to version 0.36.0
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Restore availability to Chromebooks (#932)
|
||||||
|
- Add a [documentation](./docs/integration_tests.md) to run integration tests
|
||||||
|
|
||||||
|
Changes in RiotX 0.17.0 (2020-02-27)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
|
@ -11,7 +37,8 @@ Features ✨:
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Migrate to binary QR code verification (#994)
|
- Migrate to binary QR code verification (#994)
|
||||||
- Share action is added to room profile and room member profile (#858)
|
- Share action is added to room profile and room member profile (#858)
|
||||||
- FTUE: do not display a different color when encrypting message when not in developer mode.
|
- Display avatar in fullscreen (#861)
|
||||||
|
- Fix some performance issues with crypto
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Account creation: wrongly hints that an email can be used to create an account (#941)
|
- Account creation: wrongly hints that an email can be used to create an account (#941)
|
||||||
|
@ -23,21 +50,13 @@ Bugfix 🐛:
|
||||||
- Fix some invitation handling issues (#1013)
|
- Fix some invitation handling issues (#1013)
|
||||||
- New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022)
|
- New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022)
|
||||||
- New direct chat: selecting several participants was not adding the room to the direct chats list
|
- New direct chat: selecting several participants was not adding the room to the direct chats list
|
||||||
|
- Room overview shows deleted messages as “Encrypted message” (#758)
|
||||||
Translations 🗣:
|
|
||||||
-
|
|
||||||
|
|
||||||
SDK API changes ⚠️:
|
SDK API changes ⚠️:
|
||||||
- Get crypto methods through Session.cryptoService()
|
- Get crypto methods through Session.cryptoService()
|
||||||
- ProgressListener.onProgress() function will be invoked on the background thread instead of UI thread
|
- ProgressListener.onProgress() function will be invoked on the background thread instead of UI thread
|
||||||
- Improve CreateRoomParams API (#1070)
|
- Improve CreateRoomParams API (#1070)
|
||||||
|
|
||||||
Build 🧱:
|
|
||||||
-
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
-
|
|
||||||
|
|
||||||
Changes in RiotX 0.16.0 (2020-02-14)
|
Changes in RiotX 0.16.0 (2020-02-14)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,8 @@ Make sure the following commands execute without any error:
|
||||||
RiotX is currently supported on Android KitKat (API 19+): please test your change on an Android device (or Android emulator) running with API 19. Many issues can happen (including crashes) on older devices.
|
RiotX is currently supported on Android KitKat (API 19+): please test your change on an Android device (or Android emulator) running with API 19. Many issues can happen (including crashes) on older devices.
|
||||||
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
|
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
|
||||||
|
|
||||||
|
You should consider adding Unit tests with your PR, and also integration tests (AndroidTest). Please refer to [this document](./docs/integration_tests.md) to install and run the integration test environment.
|
||||||
|
|
||||||
### Internationalisation
|
### Internationalisation
|
||||||
|
|
||||||
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/).
|
||||||
|
|
97
docs/integration_tests.md
Normal file
97
docs/integration_tests.md
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
# Integration tests
|
||||||
|
|
||||||
|
Integration tests are useful to ensure that the code works well for any use cases.
|
||||||
|
|
||||||
|
They can also be used as sample on how to use the Matrix SDK.
|
||||||
|
|
||||||
|
In a ideal world, every API of the SDK should be covered by integration tests. For the moment, we have test mainly for the Crypto part, which is the tricky part. But it covers quite a lot of features: accounts creation, login to existing account, send encrypted messages, keys backup, verification, etc.
|
||||||
|
|
||||||
|
The Matrix SDK is able to open multiple sessions, for the same user, of for different users. This way we can test communication between several sessions on a single device.
|
||||||
|
|
||||||
|
## Pre requirements
|
||||||
|
|
||||||
|
Integration tests need a homeserver running on localhost.
|
||||||
|
|
||||||
|
The documentation describes what we do to have one, using [Synapse](https://github.com/matrix-org/synapse/), which is the Matrix reference homeserver.
|
||||||
|
|
||||||
|
## Install and run Synapse
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
|
||||||
|
- Install virtualenv
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m pip install virtualenv
|
||||||
|
```
|
||||||
|
|
||||||
|
- Clone Synapse repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone -b develop https://github.com/matrix-org/synapse.git
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
git clone -b develop git@github.com:matrix-org/synapse.git
|
||||||
|
```
|
||||||
|
|
||||||
|
You should have the develop branch cloned by default.
|
||||||
|
|
||||||
|
- Run synapse, from the Synapse folder you just cloned
|
||||||
|
|
||||||
|
```bash
|
||||||
|
virtualenv -p python3 env
|
||||||
|
source env/bin/activate
|
||||||
|
pip install -e .
|
||||||
|
demo/start.sh --no-rate-limit
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, to install the latest Synapse release package (and not a cloned branch) you can run the following instead of `pip install -e .`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install matrix-synapse
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now have 3 running federated Synapse instances 🎉, at http://127.0.0.1:8080/, http://127.0.0.1:8081/ and http://127.0.0.1:8082/, which should display a "It Works! Synapse is running" message.
|
||||||
|
|
||||||
|
## Run the test
|
||||||
|
|
||||||
|
It's recommended to run tests using an Android Emulator and not a real device. First reason for that is that the tests will use http://10.0.2.2:8080 to connect to Synapse, which run locally on your machine.
|
||||||
|
|
||||||
|
You can run all the tests in the `androidTest` folders.
|
||||||
|
|
||||||
|
## Stop Synapse
|
||||||
|
|
||||||
|
To stop Synapse, you can run the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./demo/stop.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
And you can deactivate the virtualenv:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deactivate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshoot
|
||||||
|
|
||||||
|
You'll need python3 to be able to run synapse
|
||||||
|
|
||||||
|
### Android Emulator does cannot reach the homeserver
|
||||||
|
|
||||||
|
Try on the Emulator browser to open "http://10.0.2.2:8080". You should see the "Synapse is running" message.
|
||||||
|
|
||||||
|
### virtualenv command fails
|
||||||
|
|
||||||
|
You can try using
|
||||||
|
```bash
|
||||||
|
python3 -m venv env
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
python3 -m virtualenv env
|
||||||
|
```
|
||||||
|
instead of
|
||||||
|
```bash
|
||||||
|
virtualenv -p python3 env
|
||||||
|
```
|
|
@ -16,7 +16,10 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.common
|
package im.vector.matrix.android.common
|
||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Assert.fail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two lists and their content
|
* Compare two lists and their content
|
||||||
|
|
|
@ -22,7 +22,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey
|
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
|
@ -21,7 +21,11 @@ import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
|
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertNotEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Assert.fail
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.internal.crypto.crosssigning
|
||||||
|
|
||||||
|
import org.amshove.kluent.shouldBeNull
|
||||||
|
import org.amshove.kluent.shouldBeTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@Suppress("SpellCheckingInspection")
|
||||||
|
class ExtensionsKtTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testComparingBase64StringWithOrWithoutPadding() {
|
||||||
|
// Without padding
|
||||||
|
"NMJyumnhMic".fromBase64().contentEquals("NMJyumnhMic".fromBase64()).shouldBeTrue()
|
||||||
|
// With padding
|
||||||
|
"NMJyumnhMic".fromBase64().contentEquals("NMJyumnhMic=".fromBase64()).shouldBeTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBadBase64() {
|
||||||
|
"===".fromBase64Safe().shouldBeNull()
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
// Manually mark it as trusted from first session
|
// Manually mark it as trusted from first session
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.doSync<Unit> {
|
||||||
bobSession.cryptoService().crossSigningService().signDevice(bobSecondDeviceId, it)
|
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now alice should cross trust bob's second device
|
// Now alice should cross trust bob's second device
|
||||||
|
|
|
@ -20,7 +20,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import im.vector.matrix.android.common.assertByteArrayNotEqual
|
import im.vector.matrix.android.common.assertByteArrayNotEqual
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertArrayEquals
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
|
@ -16,26 +16,25 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.ssss
|
package im.vector.matrix.android.internal.crypto.ssss
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
|
|
||||||
import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
|
import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
|
||||||
import im.vector.matrix.android.api.session.securestorage.KeySigner
|
import im.vector.matrix.android.api.session.securestorage.KeySigner
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.RawBytesKeySpec
|
||||||
import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
|
import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
|
||||||
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.common.CommonTestHelper
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
import im.vector.matrix.android.common.SessionTestParams
|
import im.vector.matrix.android.common.SessionTestParams
|
||||||
import im.vector.matrix.android.common.TestConstants
|
import im.vector.matrix.android.common.TestConstants
|
||||||
import im.vector.matrix.android.common.TestMatrixCallback
|
import im.vector.matrix.android.common.TestMatrixCallback
|
||||||
import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
|
import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
|
import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||||
import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
|
|
||||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
|
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -71,7 +70,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
|
|
||||||
val TEST_KEY_ID = "my.test.Key"
|
val TEST_KEY_ID = "my.test.Key"
|
||||||
|
|
||||||
val ssssKeyCreationInfo = mTestHelper.doSync<SsssKeyCreationInfo> {
|
mTestHelper.doSync<SsssKeyCreationInfo> {
|
||||||
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
|
quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,16 +94,9 @@ class QuadSTests : InstrumentedTest {
|
||||||
assertNotNull("Key should be stored in account data", accountData)
|
assertNotNull("Key should be stored in account data", accountData)
|
||||||
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
|
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
|
||||||
assertNotNull("Key Content cannot be parsed", parsed)
|
assertNotNull("Key Content cannot be parsed", parsed)
|
||||||
assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
|
assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_AES_HMAC_SHA2, parsed!!.algorithm)
|
||||||
assertEquals("Unexpected key name", "Test Key", parsed.name)
|
assertEquals("Unexpected key name", "Test Key", parsed.name)
|
||||||
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
assertNull("Key was not generated from passphrase", parsed.passphrase)
|
||||||
assertNotNull("Pubkey should be defined", parsed.publicKey)
|
|
||||||
|
|
||||||
val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(ssssKeyCreationInfo.recoveryKey)
|
|
||||||
val pubKey = withOlmDecryption { olmPkDecryption ->
|
|
||||||
olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
|
|
||||||
}
|
|
||||||
assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
|
|
||||||
|
|
||||||
// Set as default key
|
// Set as default key
|
||||||
quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback<Unit> {})
|
quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback<Unit> {})
|
||||||
|
@ -137,13 +129,15 @@ class QuadSTests : InstrumentedTest {
|
||||||
val keyId = "My.Key"
|
val keyId = "My.Key"
|
||||||
val info = generatedSecret(aliceSession, keyId, true)
|
val info = generatedSecret(aliceSession, keyId, true)
|
||||||
|
|
||||||
|
val keySpec = RawBytesKeySpec.fromRecoveryKey(info.recoveryKey)
|
||||||
|
|
||||||
// Store a secret
|
// Store a secret
|
||||||
val clearSecret = Base64.encodeToString("42".toByteArray(), Base64.NO_PADDING or Base64.NO_WRAP)
|
val clearSecret = "42".toByteArray().toBase64NoPadding()
|
||||||
mTestHelper.doSync<Unit> {
|
mTestHelper.doSync<Unit> {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"secret.of.life",
|
"secret.of.life",
|
||||||
clearSecret,
|
clearSecret,
|
||||||
null, // default key
|
listOf(SharedSecretStorageService.KeyRef(null, keySpec)), // default key
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -157,14 +151,13 @@ class QuadSTests : InstrumentedTest {
|
||||||
val secret = EncryptedSecretContent.fromJson(encryptedContent?.get(keyId))
|
val secret = EncryptedSecretContent.fromJson(encryptedContent?.get(keyId))
|
||||||
assertNotNull(secret?.ciphertext)
|
assertNotNull(secret?.ciphertext)
|
||||||
assertNotNull(secret?.mac)
|
assertNotNull(secret?.mac)
|
||||||
assertNotNull(secret?.ephemeral)
|
assertNotNull(secret?.initializationVector)
|
||||||
|
|
||||||
// Try to decrypt??
|
// Try to decrypt??
|
||||||
|
|
||||||
val keySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(info.recoveryKey)
|
|
||||||
|
|
||||||
val decryptedSecret = mTestHelper.doSync<String> {
|
val decryptedSecret = mTestHelper.doSync<String> {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
|
aliceSession.sharedSecretStorageService.getSecret(
|
||||||
|
"secret.of.life",
|
||||||
null, // default key
|
null, // default key
|
||||||
keySpec!!,
|
keySpec!!,
|
||||||
it
|
it
|
||||||
|
@ -209,7 +202,10 @@ class QuadSTests : InstrumentedTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
listOf(keyId1, keyId2),
|
listOf(
|
||||||
|
SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)),
|
||||||
|
SharedSecretStorageService.KeyRef(keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey))
|
||||||
|
),
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -226,7 +222,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.doSync<String> {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
Curve25519AesSha2KeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
|
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -234,7 +230,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.doSync<String> {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId2,
|
keyId2,
|
||||||
Curve25519AesSha2KeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
|
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -255,7 +251,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
aliceSession.sharedSecretStorageService.storeSecret(
|
aliceSession.sharedSecretStorageService.storeSecret(
|
||||||
"my.secret",
|
"my.secret",
|
||||||
mySecretText.toByteArray().toBase64NoPadding(),
|
mySecretText.toByteArray().toBase64NoPadding(),
|
||||||
listOf(keyId1),
|
listOf(SharedSecretStorageService.KeyRef(keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey))),
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +260,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
var error = false
|
var error = false
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
Curve25519AesSha2KeySpec.fromPassphrase(
|
RawBytesKeySpec.fromPassphrase(
|
||||||
"A bad passphrase",
|
"A bad passphrase",
|
||||||
key1Info.content?.passphrase?.salt ?: "",
|
key1Info.content?.passphrase?.salt ?: "",
|
||||||
key1Info.content?.passphrase?.iterations ?: 0,
|
key1Info.content?.passphrase?.iterations ?: 0,
|
||||||
|
@ -289,7 +285,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
mTestHelper.doSync<String> {
|
mTestHelper.doSync<String> {
|
||||||
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
aliceSession.sharedSecretStorageService.getSecret("my.secret",
|
||||||
keyId1,
|
keyId1,
|
||||||
Curve25519AesSha2KeySpec.fromPassphrase(
|
RawBytesKeySpec.fromPassphrase(
|
||||||
passphrase,
|
passphrase,
|
||||||
key1Info.content?.passphrase?.salt ?: "",
|
key1Info.content?.passphrase?.salt ?: "",
|
||||||
key1Info.content?.passphrase?.iterations ?: 0,
|
key1Info.content?.passphrase?.iterations ?: 0,
|
||||||
|
|
|
@ -19,14 +19,14 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
import im.vector.matrix.android.api.session.crypto.verification.SasMode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.common.CommonTestHelper
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
|
|
|
@ -18,8 +18,8 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
|
||||||
import im.vector.matrix.android.common.CommonTestHelper
|
import im.vector.matrix.android.common.CommonTestHelper
|
||||||
import im.vector.matrix.android.common.CryptoTestHelper
|
import im.vector.matrix.android.common.CryptoTestHelper
|
||||||
import im.vector.matrix.android.common.TestConstants
|
import im.vector.matrix.android.common.TestConstants
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.crypto
|
package im.vector.matrix.android.api.crypto
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
|
||||||
import im.vector.matrix.android.internal.crypto.verification.getEmojiForCode
|
import im.vector.matrix.android.internal.crypto.verification.getEmojiForCode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.api.extensions
|
package im.vector.matrix.android.api.extensions
|
||||||
|
|
||||||
import im.vector.matrix.android.api.comparators.DatedObjectComparators
|
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
|
|
||||||
|
@ -33,7 +32,5 @@ fun CryptoDeviceInfo.getFingerprintHumanReadable() = fingerprint()
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
fun List<DeviceInfo>.sortByLastSeen(): List<DeviceInfo> {
|
fun List<DeviceInfo>.sortByLastSeen(): List<DeviceInfo> {
|
||||||
val list = toMutableList()
|
return this.sortedByDescending { it.lastSeenTs ?: 0 }
|
||||||
list.sortWith(DatedObjectComparators.descComparator)
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||||
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
|
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
|
|
|
@ -42,6 +42,10 @@ interface CrossSigningService {
|
||||||
fun initializeCrossSigning(authParams: UserPasswordAuth?,
|
fun initializeCrossSigning(authParams: UserPasswordAuth?,
|
||||||
callback: MatrixCallback<Unit>? = null)
|
callback: MatrixCallback<Unit>? = null)
|
||||||
|
|
||||||
|
fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
|
||||||
|
uskKeyPrivateKey: String?,
|
||||||
|
sskPrivateKey: String?) : UserTrustResult
|
||||||
|
|
||||||
fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo?
|
fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo?
|
||||||
|
|
||||||
fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>>
|
fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>>
|
||||||
|
@ -53,11 +57,13 @@ interface CrossSigningService {
|
||||||
fun trustUser(otherUserId: String,
|
fun trustUser(otherUserId: String,
|
||||||
callback: MatrixCallback<Unit>)
|
callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
|
fun markMyMasterKeyAsTrusted()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign one of your devices and upload the signature
|
* Sign one of your devices and upload the signature
|
||||||
*/
|
*/
|
||||||
fun signDevice(deviceId: String,
|
fun trustDevice(deviceId: String,
|
||||||
callback: MatrixCallback<Unit>)
|
callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
fun checkDeviceTrust(otherUserId: String,
|
fun checkDeviceTrust(otherUserId: String,
|
||||||
otherDeviceId: String,
|
otherDeviceId: String,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 New Vector Ltd
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,16 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotx.features.crypto.verification
|
package im.vector.matrix.android.api.session.crypto.crosssigning
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
const val MASTER_KEY_SSSS_NAME = "m.cross_signing.master"
|
||||||
|
|
||||||
val supportedVerificationMethods =
|
const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing"
|
||||||
listOf(
|
|
||||||
// RiotX supports SAS verification
|
const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing"
|
||||||
VerificationMethod.SAS,
|
|
||||||
// RiotX is able to show QR codes
|
|
||||||
VerificationMethod.QR_CODE_SHOW,
|
|
||||||
// RiotX is able to scan QR codes
|
|
||||||
VerificationMethod.QR_CODE_SCAN
|
|
||||||
)
|
|
|
@ -14,8 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO Rename package
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
|
||||||
|
|
||||||
enum class CancelCode(val value: String, val humanReadable: String) {
|
enum class CancelCode(val value: String, val humanReadable: String) {
|
||||||
User("m.user", "the user cancelled the verification"),
|
User("m.user", "the user cancelled the verification"),
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
interface IncomingSasVerificationTransaction : SasVerificationTransaction {
|
interface IncomingSasVerificationTransaction : SasVerificationTransaction {
|
||||||
val uxState: UxState
|
val uxState: UxState
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
interface OutgoingSasVerificationTransaction : SasVerificationTransaction {
|
interface OutgoingSasVerificationTransaction : SasVerificationTransaction {
|
||||||
val uxState: UxState
|
val uxState: UxState
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
interface QrCodeVerificationTransaction : VerificationTransaction {
|
interface QrCodeVerificationTransaction : VerificationTransaction {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
object SasMode {
|
object SasMode {
|
||||||
const val DECIMAL = "decimal"
|
const val DECIMAL = "decimal"
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
interface SasVerificationTransaction : VerificationTransaction {
|
interface SasVerificationTransaction : VerificationTransaction {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verification methods
|
* Verification methods
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
interface VerificationTransaction {
|
interface VerificationTransaction {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.android.api.session.crypto.sas
|
package im.vector.matrix.android.api.session.crypto.verification
|
||||||
|
|
||||||
sealed class VerificationTxState {
|
sealed class VerificationTxState {
|
||||||
// Uninitialized state
|
// Uninitialized state
|
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.room.model.message
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.events.model.RelationType
|
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
||||||
|
|
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.room.model.message
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
import im.vector.matrix.android.api.session.crypto.verification.SasMode
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
||||||
|
|
|
@ -32,7 +32,8 @@ data class EncryptedSecretContent(
|
||||||
/** unpadded base64-encoded ciphertext */
|
/** unpadded base64-encoded ciphertext */
|
||||||
@Json(name = "ciphertext") val ciphertext: String? = null,
|
@Json(name = "ciphertext") val ciphertext: String? = null,
|
||||||
@Json(name = "mac") val mac: String? = null,
|
@Json(name = "mac") val mac: String? = null,
|
||||||
@Json(name = "ephemeral") val ephemeral: String? = null
|
@Json(name = "ephemeral") val ephemeral: String? = null,
|
||||||
|
@Json(name = "iv") val initializationVector: String? = null
|
||||||
) : AccountDataContent {
|
) : AccountDataContent {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.matrix.android.api.session.securestorage
|
||||||
|
|
||||||
|
sealed class IntegrityResult {
|
||||||
|
data class Success(val passphraseBased: Boolean) : IntegrityResult()
|
||||||
|
data class Error(val cause: SharedSecretStorageError) : IntegrityResult()
|
||||||
|
}
|
|
@ -27,5 +27,8 @@ sealed class SharedSecretStorageError(message: String?) : Throwable(message) {
|
||||||
|
|
||||||
object BadKeyFormat : SharedSecretStorageError("Bad Key Format")
|
object BadKeyFormat : SharedSecretStorageError("Bad Key Format")
|
||||||
object ParsingError : SharedSecretStorageError("parsing Error")
|
object ParsingError : SharedSecretStorageError("parsing Error")
|
||||||
|
object BadMac : SharedSecretStorageError("Bad mac")
|
||||||
|
object BadCipherText : SharedSecretStorageError("Bad cipher text")
|
||||||
|
|
||||||
data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage)
|
data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ interface SharedSecretStorageService {
|
||||||
*/
|
*/
|
||||||
fun generateKey(keyId: String,
|
fun generateKey(keyId: String,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
keySigner: KeySigner,
|
keySigner: KeySigner?,
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>)
|
callback: MatrixCallback<SsssKeyCreationInfo>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,7 +92,7 @@ interface SharedSecretStorageService {
|
||||||
* @param secret The secret contents.
|
* @param secret The secret contents.
|
||||||
* @param keys The list of (ID,privateKey) of the keys to use to encrypt the secret.
|
* @param keys The list of (ID,privateKey) of the keys to use to encrypt the secret.
|
||||||
*/
|
*/
|
||||||
fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>)
|
fun storeSecret(name: String, secretBase64: String, keys: List<KeyRef>, callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this call to determine which SSSSKeySpec to use for requesting secret
|
* Use this call to determine which SSSSKeySpec to use for requesting secret
|
||||||
|
@ -104,9 +104,15 @@ interface SharedSecretStorageService {
|
||||||
*
|
*
|
||||||
* @param name The name of the secret
|
* @param name The name of the secret
|
||||||
* @param keyId The id of the key that should be used to decrypt (null for default key)
|
* @param keyId The id of the key that should be used to decrypt (null for default key)
|
||||||
* @param secretKey the secret key to use (@see #Curve25519AesSha2KeySpec)
|
* @param secretKey the secret key to use (@see #RawBytesKeySpec)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Throws
|
|
||||||
fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback<String>)
|
fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback<String>)
|
||||||
|
|
||||||
|
fun checkShouldBeAbleToAccessSecrets(secretNames: List<String>, keyId: String?) : IntegrityResult
|
||||||
|
|
||||||
|
data class KeyRef(
|
||||||
|
val keyId: String?,
|
||||||
|
val keySpec: SsssKeySpec?
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,14 @@ import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyF
|
||||||
/** Tag class */
|
/** Tag class */
|
||||||
interface SsssKeySpec
|
interface SsssKeySpec
|
||||||
|
|
||||||
data class Curve25519AesSha2KeySpec(
|
data class RawBytesKeySpec(
|
||||||
val privateKey: ByteArray
|
val privateKey: ByteArray
|
||||||
) : SsssKeySpec {
|
) : SsssKeySpec {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun fromPassphrase(passphrase: String, salt: String, iterations: Int, progressListener: ProgressListener?): Curve25519AesSha2KeySpec {
|
fun fromPassphrase(passphrase: String, salt: String, iterations: Int, progressListener: ProgressListener?): RawBytesKeySpec {
|
||||||
return Curve25519AesSha2KeySpec(
|
return RawBytesKeySpec(
|
||||||
privateKey = deriveKey(
|
privateKey = deriveKey(
|
||||||
passphrase,
|
passphrase,
|
||||||
salt,
|
salt,
|
||||||
|
@ -40,9 +40,9 @@ data class Curve25519AesSha2KeySpec(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRecoveryKey(recoveryKey: String): Curve25519AesSha2KeySpec? {
|
fun fromRecoveryKey(recoveryKey: String): RawBytesKeySpec? {
|
||||||
return extractCurveKeyFromRecoveryKey(recoveryKey)?.let {
|
return extractCurveKeyFromRecoveryKey(recoveryKey)?.let {
|
||||||
Curve25519AesSha2KeySpec(
|
RawBytesKeySpec(
|
||||||
privateKey = it
|
privateKey = it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ data class Curve25519AesSha2KeySpec(
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
other as Curve25519AesSha2KeySpec
|
other as RawBytesKeySpec
|
||||||
|
|
||||||
if (!privateKey.contentEquals(other.privateKey)) return false
|
if (!privateKey.contentEquals(other.privateKey)) return false
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,19 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||||
import im.vector.matrix.android.internal.auth.data.RiotConfig
|
import im.vector.matrix.android.internal.auth.data.RiotConfig
|
||||||
import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed
|
import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed
|
||||||
import im.vector.matrix.android.internal.auth.registration.*
|
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.RegistrationParams
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.SuccessResult
|
||||||
|
import im.vector.matrix.android.internal.auth.registration.ValidationCodeBody
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Headers
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Url
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The login REST API.
|
* The login REST API.
|
||||||
|
|
|
@ -20,7 +20,13 @@ import android.net.Uri
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.AuthenticationService
|
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||||
import im.vector.matrix.android.api.auth.data.*
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
|
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||||
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.api.auth.data.Versions
|
||||||
|
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
|
||||||
|
import im.vector.matrix.android.api.auth.data.isSupportedBySdk
|
||||||
import im.vector.matrix.android.api.auth.login.LoginWizard
|
import im.vector.matrix.android.api.auth.login.LoginWizard
|
||||||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.auth.db
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.internal.auth.login.ResetPasswordData
|
import im.vector.matrix.android.internal.auth.login.ResetPasswordData
|
||||||
import im.vector.matrix.android.internal.auth.registration.ThreePidData
|
import im.vector.matrix.android.internal.auth.registration.ThreePidData
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds all pending data when creating a session, either by login or by register
|
* This class holds all pending data when creating a session, either by login or by register
|
||||||
|
|
|
@ -35,6 +35,8 @@ const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-
|
||||||
* Secured Shared Storage algorithm constant
|
* Secured Shared Storage algorithm constant
|
||||||
*/
|
*/
|
||||||
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
|
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
|
||||||
|
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
|
||||||
|
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"
|
||||||
|
|
||||||
// TODO Refacto: use this constants everywhere
|
// TODO Refacto: use this constants everywhere
|
||||||
const val ed25519 = "ed25519"
|
const val ed25519 = "ed25519"
|
||||||
|
|
|
@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
|
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare
|
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
|
@ -51,11 +50,10 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
|
||||||
* @param event the announcement event.
|
* @param event the announcement event.
|
||||||
*/
|
*/
|
||||||
fun onRoomKeyRequestEvent(event: Event) {
|
fun onRoomKeyRequestEvent(event: Event) {
|
||||||
val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>()
|
when (val roomKeyShareAction = event.getClearContent()?.get("action") as? String) {
|
||||||
when (roomKeyShare?.action) {
|
|
||||||
RoomKeyShare.ACTION_SHARE_REQUEST -> IncomingRoomKeyRequest.fromEvent(event)?.let { receivedRoomKeyRequests.add(it) }
|
RoomKeyShare.ACTION_SHARE_REQUEST -> IncomingRoomKeyRequest.fromEvent(event)?.let { receivedRoomKeyRequests.add(it) }
|
||||||
RoomKeyShare.ACTION_SHARE_CANCELLATION -> IncomingRoomKeyRequestCancellation.fromEvent(event)?.let { receivedRoomKeyRequestCancellations.add(it) }
|
RoomKeyShare.ACTION_SHARE_CANCELLATION -> IncomingRoomKeyRequestCancellation.fromEvent(event)?.let { receivedRoomKeyRequestCancellations.add(it) }
|
||||||
else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action ${roomKeyShare?.action}")
|
else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action $roomKeyShareAction")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,12 @@ import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||||
import im.vector.matrix.android.internal.util.convertFromUTF8
|
import im.vector.matrix.android.internal.util.convertFromUTF8
|
||||||
import im.vector.matrix.android.internal.util.convertToUTF8
|
import im.vector.matrix.android.internal.util.convertToUTF8
|
||||||
import org.matrix.olm.*
|
import org.matrix.olm.OlmAccount
|
||||||
|
import org.matrix.olm.OlmException
|
||||||
|
import org.matrix.olm.OlmMessage
|
||||||
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
|
import org.matrix.olm.OlmSession
|
||||||
|
import org.matrix.olm.OlmUtility
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -16,10 +16,29 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.crypto.api
|
package im.vector.matrix.android.internal.crypto.api
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysClaimResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.UploadSigningKeysBody
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.HTTP
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
internal interface CryptoApi {
|
internal interface CryptoApi {
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface ComputeTrustTask : Task<ComputeTrustTask.Params, RoomEncryptionTrustLevel> {
|
internal interface ComputeTrustTask : Task<ComputeTrustTask.Params, RoomEncryptionTrustLevel> {
|
||||||
|
@ -29,14 +31,15 @@ internal interface ComputeTrustTask : Task<ComputeTrustTask.Params, RoomEncrypti
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultComputeTrustTask @Inject constructor(
|
internal class DefaultComputeTrustTask @Inject constructor(
|
||||||
val cryptoStore: IMXCryptoStore
|
private val cryptoStore: IMXCryptoStore,
|
||||||
|
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
||||||
) : ComputeTrustTask {
|
) : ComputeTrustTask {
|
||||||
|
|
||||||
override suspend fun execute(params: ComputeTrustTask.Params): RoomEncryptionTrustLevel {
|
override suspend fun execute(params: ComputeTrustTask.Params): RoomEncryptionTrustLevel = withContext(coroutineDispatchers.crypto) {
|
||||||
val allTrustedUserIds = params.userIds
|
val allTrustedUserIds = params.userIds
|
||||||
.filter { userId -> getUserCrossSigningKeys(userId)?.isTrusted() == true }
|
.filter { userId -> getUserCrossSigningKeys(userId)?.isTrusted() == true }
|
||||||
|
|
||||||
return if (allTrustedUserIds.isEmpty()) {
|
if (allTrustedUserIds.isEmpty()) {
|
||||||
RoomEncryptionTrustLevel.Default
|
RoomEncryptionTrustLevel.Default
|
||||||
} else {
|
} else {
|
||||||
// If one of the verified user as an untrusted device -> warning
|
// If one of the verified user as an untrusted device -> warning
|
||||||
|
|
|
@ -80,7 +80,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
|
|
||||||
cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeysInfo ->
|
cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeysInfo ->
|
||||||
privateKeysInfo.master
|
privateKeysInfo.master
|
||||||
?.fromBase64NoPadding()
|
?.fromBase64()
|
||||||
?.let { privateKeySeed ->
|
?.let { privateKeySeed ->
|
||||||
val pkSigning = OlmPkSigning()
|
val pkSigning = OlmPkSigning()
|
||||||
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
|
||||||
|
@ -88,11 +88,12 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
Timber.i("## CrossSigning - Loading master key success")
|
Timber.i("## CrossSigning - Loading master key success")
|
||||||
} else {
|
} else {
|
||||||
Timber.w("## CrossSigning - Public master key does not match the private key")
|
Timber.w("## CrossSigning - Public master key does not match the private key")
|
||||||
// TODO untrust
|
pkSigning.releaseSigning()
|
||||||
|
// TODO untrust?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
privateKeysInfo.user
|
privateKeysInfo.user
|
||||||
?.fromBase64NoPadding()
|
?.fromBase64()
|
||||||
?.let { privateKeySeed ->
|
?.let { privateKeySeed ->
|
||||||
val pkSigning = OlmPkSigning()
|
val pkSigning = OlmPkSigning()
|
||||||
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
|
||||||
|
@ -100,11 +101,12 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
Timber.i("## CrossSigning - Loading User Signing key success")
|
Timber.i("## CrossSigning - Loading User Signing key success")
|
||||||
} else {
|
} else {
|
||||||
Timber.w("## CrossSigning - Public User key does not match the private key")
|
Timber.w("## CrossSigning - Public User key does not match the private key")
|
||||||
// TODO untrust
|
pkSigning.releaseSigning()
|
||||||
|
// TODO untrust?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
privateKeysInfo.selfSigned
|
privateKeysInfo.selfSigned
|
||||||
?.fromBase64NoPadding()
|
?.fromBase64()
|
||||||
?.let { privateKeySeed ->
|
?.let { privateKeySeed ->
|
||||||
val pkSigning = OlmPkSigning()
|
val pkSigning = OlmPkSigning()
|
||||||
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
|
||||||
|
@ -112,7 +114,8 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
Timber.i("## CrossSigning - Loading Self Signing key success")
|
Timber.i("## CrossSigning - Loading Self Signing key success")
|
||||||
} else {
|
} else {
|
||||||
Timber.w("## CrossSigning - Public Self Signing key does not match the private key")
|
Timber.w("## CrossSigning - Public Self Signing key does not match the private key")
|
||||||
// TODO untrust
|
pkSigning.releaseSigning()
|
||||||
|
// TODO untrust?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,16 +227,18 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
val myDevice = myDeviceInfoHolder.get().myDevice
|
val myDevice = myDeviceInfoHolder.get().myDevice
|
||||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())
|
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())
|
||||||
val signedDevice = selfSigningPkOlm.sign(canonicalJson)
|
val signedDevice = selfSigningPkOlm.sign(canonicalJson)
|
||||||
val updateSignatures = (myDevice.signatures?.toMutableMap() ?: HashMap()).also {
|
val updateSignatures = (myDevice.signatures?.toMutableMap() ?: HashMap())
|
||||||
it[userId] = (it[userId]
|
.also {
|
||||||
?: HashMap()) + mapOf("ed25519:$sskPublicKey" to signedDevice)
|
it[userId] = (it[userId]
|
||||||
}
|
?: HashMap()) + mapOf("ed25519:$sskPublicKey" to signedDevice)
|
||||||
|
}
|
||||||
myDevice.copy(signatures = updateSignatures).let {
|
myDevice.copy(signatures = updateSignatures).let {
|
||||||
uploadSignatureQueryBuilder.withDeviceInfo(it)
|
uploadSignatureQueryBuilder.withDeviceInfo(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign MSK with device key (migration) and upload signatures
|
// sign MSK with device key (migration) and upload signatures
|
||||||
olmDevice.signMessage(JsonCanonicalizer.getCanonicalJson(Map::class.java, mskCrossSigningKeyInfo.signalableJSONDictionary()))?.let { sign ->
|
val message = JsonCanonicalizer.getCanonicalJson(Map::class.java, mskCrossSigningKeyInfo.signalableJSONDictionary())
|
||||||
|
olmDevice.signMessage(message)?.let { sign ->
|
||||||
val mskUpdatedSignatures = (mskCrossSigningKeyInfo.signatures?.toMutableMap()
|
val mskUpdatedSignatures = (mskCrossSigningKeyInfo.signatures?.toMutableMap()
|
||||||
?: HashMap()).also {
|
?: HashMap()).also {
|
||||||
it[userId] = (it[userId]
|
it[userId] = (it[userId]
|
||||||
|
@ -292,6 +297,80 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
cryptoStore.clearOtherUserTrust()
|
cryptoStore.clearOtherUserTrust()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
|
||||||
|
uskKeyPrivateKey: String?,
|
||||||
|
sskPrivateKey: String?
|
||||||
|
): UserTrustResult {
|
||||||
|
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||||
|
|
||||||
|
var masterKeyIsTrusted = false
|
||||||
|
var userKeyIsTrusted = false
|
||||||
|
var selfSignedKeyIsTrusted = false
|
||||||
|
|
||||||
|
masterKeyPrivateKey?.fromBase64()
|
||||||
|
?.let { privateKeySeed ->
|
||||||
|
val pkSigning = OlmPkSigning()
|
||||||
|
try {
|
||||||
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
|
||||||
|
masterPkSigning?.releaseSigning()
|
||||||
|
masterPkSigning = pkSigning
|
||||||
|
masterKeyIsTrusted = true
|
||||||
|
Timber.i("## CrossSigning - Loading master key success")
|
||||||
|
} else {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uskKeyPrivateKey?.fromBase64()
|
||||||
|
?.let { privateKeySeed ->
|
||||||
|
val pkSigning = OlmPkSigning()
|
||||||
|
try {
|
||||||
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
|
||||||
|
userPkSigning?.releaseSigning()
|
||||||
|
userPkSigning = pkSigning
|
||||||
|
userKeyIsTrusted = true
|
||||||
|
Timber.i("## CrossSigning - Loading master key success")
|
||||||
|
} else {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sskPrivateKey?.fromBase64()
|
||||||
|
?.let { privateKeySeed ->
|
||||||
|
val pkSigning = OlmPkSigning()
|
||||||
|
try {
|
||||||
|
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
|
||||||
|
selfSigningPkSigning?.releaseSigning()
|
||||||
|
selfSigningPkSigning = pkSigning
|
||||||
|
selfSignedKeyIsTrusted = true
|
||||||
|
Timber.i("## CrossSigning - Loading master key success")
|
||||||
|
} else {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
pkSigning.releaseSigning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!masterKeyIsTrusted || !userKeyIsTrusted || !selfSignedKeyIsTrusted) {
|
||||||
|
return UserTrustResult.KeysNotTrusted(mxCrossSigningInfo)
|
||||||
|
} else {
|
||||||
|
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
|
||||||
|
val checkSelfTrust = checkSelfTrust()
|
||||||
|
if (checkSelfTrust.isVerified()) {
|
||||||
|
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey, uskKeyPrivateKey, sskPrivateKey)
|
||||||
|
setUserKeysAsTrusted(userId, true)
|
||||||
|
}
|
||||||
|
return checkSelfTrust
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
|
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
|
||||||
|
@ -371,10 +450,12 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
// 1) check if I know the private key
|
// 1) check if I know the private key
|
||||||
val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()
|
val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()
|
||||||
?.master
|
?.master
|
||||||
?.fromBase64NoPadding()
|
?.fromBase64()
|
||||||
|
|
||||||
var isMaterKeyTrusted = false
|
var isMaterKeyTrusted = false
|
||||||
if (masterPrivateKey != null) {
|
if (myMasterKey.trustLevel?.locallyVerified == true) {
|
||||||
|
isMaterKeyTrusted = true
|
||||||
|
} else if (masterPrivateKey != null) {
|
||||||
// Check if private match public
|
// Check if private match public
|
||||||
var olmPkSigning: OlmPkSigning? = null
|
var olmPkSigning: OlmPkSigning? = null
|
||||||
try {
|
try {
|
||||||
|
@ -507,7 +588,12 @@ internal class DefaultCrossSigningService @Inject constructor(
|
||||||
}.executeBy(taskExecutor)
|
}.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun signDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
override fun markMyMasterKeyAsTrusted() {
|
||||||
|
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
|
||||||
|
checkSelfTrust()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||||
// This device should be yours
|
// This device should be yours
|
||||||
val device = cryptoStore.getUserDevice(userId, deviceId)
|
val device = cryptoStore.getUserDevice(userId, deviceId)
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.util.Base64
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
fun CryptoDeviceInfo.canonicalSignable(): String {
|
fun CryptoDeviceInfo.canonicalSignable(): String {
|
||||||
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
|
||||||
|
@ -32,6 +33,18 @@ fun ByteArray.toBase64NoPadding(): String {
|
||||||
return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP)
|
return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.fromBase64NoPadding(): ByteArray {
|
fun String.fromBase64(): ByteArray {
|
||||||
return Base64.decode(this, Base64.NO_PADDING or Base64.NO_WRAP)
|
return Base64.decode(this, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source
|
||||||
|
*/
|
||||||
|
fun String.fromBase64Safe(): ByteArray? {
|
||||||
|
return try {
|
||||||
|
Base64.decode(this, Base64.DEFAULT)
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
Timber.e(throwable, "Unable to decode base64 string")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,18 @@ package im.vector.matrix.android.internal.crypto.crosssigning
|
||||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
|
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.CryptoDatabase
|
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -36,7 +38,7 @@ internal class ShieldTrustUpdater @Inject constructor(
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
private val computeTrustTask: ComputeTrustTask,
|
private val computeTrustTask: ComputeTrustTask,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
@CryptoDatabase private val cryptoRealmConfiguration: RealmConfiguration,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
@SessionDatabase private val sessionRealmConfiguration: RealmConfiguration,
|
@SessionDatabase private val sessionRealmConfiguration: RealmConfiguration,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater
|
private val roomSummaryUpdater: RoomSummaryUpdater
|
||||||
) {
|
) {
|
||||||
|
@ -45,29 +47,14 @@ internal class ShieldTrustUpdater @Inject constructor(
|
||||||
private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD")
|
private val BACKGROUND_HANDLER = createBackgroundHandler("SHIELD_CRYPTO_DB_THREAD")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val backgroundCryptoRealm = AtomicReference<Realm>()
|
|
||||||
private val backgroundSessionRealm = AtomicReference<Realm>()
|
private val backgroundSessionRealm = AtomicReference<Realm>()
|
||||||
|
|
||||||
private val isStarted = AtomicBoolean()
|
private val isStarted = AtomicBoolean()
|
||||||
|
|
||||||
// private var cryptoDevicesResult: RealmResults<DeviceInfoEntity>? = null
|
|
||||||
|
|
||||||
// private val cryptoDeviceChangeListener = object : OrderedRealmCollectionChangeListener<RealmResults<DeviceInfoEntity>> {
|
|
||||||
// override fun onChange(t: RealmResults<DeviceInfoEntity>, changeSet: OrderedCollectionChangeSet) {
|
|
||||||
// val grouped = t.groupBy { it.userId }
|
|
||||||
// onCryptoDevicesChange(grouped.keys.mapNotNull { it })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
if (isStarted.compareAndSet(false, true)) {
|
if (isStarted.compareAndSet(false, true)) {
|
||||||
eventBus.register(this)
|
eventBus.register(this)
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
val cryptoRealm = Realm.getInstance(cryptoRealmConfiguration)
|
|
||||||
backgroundCryptoRealm.set(cryptoRealm)
|
|
||||||
// cryptoDevicesResult = cryptoRealm.where<DeviceInfoEntity>().findAll()
|
|
||||||
// cryptoDevicesResult?.addChangeListener(cryptoDeviceChangeListener)
|
|
||||||
|
|
||||||
backgroundSessionRealm.set(Realm.getInstance(sessionRealmConfiguration))
|
backgroundSessionRealm.set(Realm.getInstance(sessionRealmConfiguration))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,10 +64,6 @@ internal class ShieldTrustUpdater @Inject constructor(
|
||||||
if (isStarted.compareAndSet(true, false)) {
|
if (isStarted.compareAndSet(true, false)) {
|
||||||
eventBus.unregister(this)
|
eventBus.unregister(this)
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
// cryptoDevicesResult?.removeAllChangeListeners()
|
|
||||||
backgroundCryptoRealm.getAndSet(null).also {
|
|
||||||
it?.close()
|
|
||||||
}
|
|
||||||
backgroundSessionRealm.getAndSet(null).also {
|
backgroundSessionRealm.getAndSet(null).also {
|
||||||
it?.close()
|
it?.close()
|
||||||
}
|
}
|
||||||
|
@ -93,8 +76,7 @@ internal class ShieldTrustUpdater @Inject constructor(
|
||||||
if (!isStarted.get()) {
|
if (!isStarted.get()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
taskExecutor.executorScope.launch(coroutineDispatchers.crypto) {
|
||||||
taskExecutor.executorScope.launch {
|
|
||||||
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(update.userIds))
|
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(update.userIds))
|
||||||
// We need to send that back to session base
|
// We need to send that back to session base
|
||||||
|
|
||||||
|
@ -117,29 +99,38 @@ internal class ShieldTrustUpdater @Inject constructor(
|
||||||
|
|
||||||
private fun onCryptoDevicesChange(users: List<String>) {
|
private fun onCryptoDevicesChange(users: List<String>) {
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
val impactedRoomsId = backgroundSessionRealm.get().where(RoomMemberSummaryEntity::class.java)
|
val impactedRoomsId = backgroundSessionRealm.get()?.where(RoomMemberSummaryEntity::class.java)
|
||||||
.`in`(RoomMemberSummaryEntityFields.USER_ID, users.toTypedArray())
|
?.`in`(RoomMemberSummaryEntityFields.USER_ID, users.toTypedArray())
|
||||||
.findAll()
|
?.findAll()
|
||||||
.map { it.roomId }
|
?.map { it.roomId }
|
||||||
.distinct()
|
?.distinct()
|
||||||
|
|
||||||
val map = HashMap<String, List<String>>()
|
val map = HashMap<String, List<String>>()
|
||||||
impactedRoomsId.forEach { roomId ->
|
impactedRoomsId?.forEach { roomId ->
|
||||||
RoomMemberSummaryEntity.where(backgroundSessionRealm.get(), roomId)
|
backgroundSessionRealm.get()?.let { realm ->
|
||||||
.findAll()
|
RoomMemberSummaryEntity.where(realm, roomId)
|
||||||
.let { results ->
|
.findAll()
|
||||||
map[roomId] = results.map { it.userId }
|
.let { results ->
|
||||||
}
|
map[roomId] = results.map { it.userId }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
map.forEach { entry ->
|
map.forEach { entry ->
|
||||||
val roomId = entry.key
|
val roomId = entry.key
|
||||||
val userList = entry.value
|
val userList = entry.value
|
||||||
taskExecutor.executorScope.launch {
|
taskExecutor.executorScope.launch {
|
||||||
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(userList))
|
withContext(coroutineDispatchers.crypto) {
|
||||||
BACKGROUND_HANDLER.post {
|
try {
|
||||||
backgroundSessionRealm.get()?.executeTransaction { realm ->
|
// Can throw if the crypto database has been closed in between, in this case log and ignore?
|
||||||
roomSummaryUpdater.updateShieldTrust(realm, roomId, updatedTrust)
|
val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(userList))
|
||||||
|
BACKGROUND_HANDLER.post {
|
||||||
|
backgroundSessionRealm.get()?.executeTransaction { realm ->
|
||||||
|
roomSummaryUpdater.updateShieldTrust(realm, roomId, updatedTrust)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,23 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.api
|
package im.vector.matrix.android.internal.crypto.keysbackup.api
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.*
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.DELETE
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ref: https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md
|
* Ref: https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md
|
||||||
|
|
|
@ -28,10 +28,6 @@ data class CryptoDeviceInfo(
|
||||||
override val keys: Map<String, String>? = null,
|
override val keys: Map<String, String>? = null,
|
||||||
override val signatures: Map<String, Map<String, String>>? = null,
|
override val signatures: Map<String, Map<String, String>>? = null,
|
||||||
val unsigned: JsonDict? = null,
|
val unsigned: JsonDict? = null,
|
||||||
|
|
||||||
// TODO how to store if this device is verified by a user SSK, or is legacy trusted?
|
|
||||||
// I need to know if it is trusted via cross signing (Trusted because bob verified it)
|
|
||||||
|
|
||||||
var trustLevel: DeviceTrustLevel? = null,
|
var trustLevel: DeviceTrustLevel? = null,
|
||||||
var isBlocked: Boolean = false
|
var isBlocked: Boolean = false
|
||||||
) : CryptoInfo {
|
) : CryptoInfo {
|
||||||
|
@ -75,19 +71,6 @@ data class CryptoDeviceInfo(
|
||||||
keys?.let { map["keys"] = it }
|
keys?.let { map["keys"] = it }
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * @return a dictionary of the parameters
|
|
||||||
// */
|
|
||||||
// fun toDeviceKeys(): DeviceKeys {
|
|
||||||
// return DeviceKeys(
|
|
||||||
// userId = userId,
|
|
||||||
// deviceId = deviceId,
|
|
||||||
// algorithms = algorithms!!,
|
|
||||||
// keys = keys!!,
|
|
||||||
// signatures = signatures!!
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun CryptoDeviceInfo.toRest(): RestDeviceInfo {
|
internal fun CryptoDeviceInfo.toRest(): RestDeviceInfo {
|
||||||
|
|
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.crypto.model.rest
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoCancel
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoCancel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,8 +17,8 @@ package im.vector.matrix.android.internal.crypto.model.rest
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoKeyFactory
|
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoKey
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoKey
|
||||||
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoKeyFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent by both devices to send their ephemeral Curve25519 public key to the other device.
|
* Sent by both devices to send their ephemeral Curve25519 public key to the other device.
|
||||||
|
|
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.crypto.model.rest
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
import im.vector.matrix.android.api.session.crypto.verification.SasMode
|
||||||
import im.vector.matrix.android.internal.crypto.verification.SASDefaultVerificationTransaction
|
import im.vector.matrix.android.internal.crypto.verification.SASDefaultVerificationTransaction
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
|
||||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||||
|
|
|
@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.model.rest
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare.Companion.ACTION_SHARE_CANCELLATION
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing a room key request cancellation content
|
* Class representing a room key request cancellation content
|
||||||
|
@ -25,7 +24,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare.Companio
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class RoomKeyShareCancellation(
|
internal data class RoomKeyShareCancellation(
|
||||||
@Json(name = "action")
|
@Json(name = "action")
|
||||||
override val action: String? = ACTION_SHARE_CANCELLATION,
|
override val action: String? = RoomKeyShare.ACTION_SHARE_CANCELLATION,
|
||||||
|
|
||||||
@Json(name = "requesting_device_id")
|
@Json(name = "requesting_device_id")
|
||||||
override val requestingDeviceId: String? = null,
|
override val requestingDeviceId: String? = null,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.model.rest
|
package im.vector.matrix.android.internal.crypto.model.rest
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
|
||||||
|
|
||||||
internal const val VERIFICATION_METHOD_SAS = "m.sas.v1"
|
internal const val VERIFICATION_METHOD_SAS = "m.sas.v1"
|
||||||
|
|
||||||
|
|
|
@ -17,37 +17,42 @@
|
||||||
package im.vector.matrix.android.internal.crypto.secrets
|
package im.vector.matrix.android.internal.crypto.secrets
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
import im.vector.matrix.android.api.session.accountdata.AccountDataService
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
|
|
||||||
import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
|
import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.IntegrityResult
|
||||||
import im.vector.matrix.android.api.session.securestorage.KeyInfo
|
import im.vector.matrix.android.api.session.securestorage.KeyInfo
|
||||||
import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
|
import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
|
||||||
import im.vector.matrix.android.api.session.securestorage.KeySigner
|
import im.vector.matrix.android.api.session.securestorage.KeySigner
|
||||||
import im.vector.matrix.android.api.session.securestorage.SsssKeySpec
|
import im.vector.matrix.android.api.session.securestorage.RawBytesKeySpec
|
||||||
import im.vector.matrix.android.api.session.securestorage.SsssPassphrase
|
|
||||||
import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
|
import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
|
||||||
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError
|
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError
|
||||||
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
|
import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
|
||||||
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SsssKeySpec
|
||||||
|
import im.vector.matrix.android.api.session.securestorage.SsssPassphrase
|
||||||
|
import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
|
import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
|
||||||
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||||
|
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword
|
import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
|
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
|
||||||
|
import im.vector.matrix.android.internal.crypto.tools.HkdfSha256
|
||||||
import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
|
import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
|
||||||
import im.vector.matrix.android.internal.crypto.tools.withOlmEncryption
|
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.olm.OlmPkMessage
|
import org.matrix.olm.OlmPkMessage
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.experimental.and
|
||||||
private data class Key(
|
|
||||||
val publicKey: String,
|
|
||||||
@Suppress("ArrayInDataClass")
|
|
||||||
val privateKey: ByteArray
|
|
||||||
)
|
|
||||||
|
|
||||||
internal class DefaultSharedSecretStorageService @Inject constructor(
|
internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
private val accountDataService: AccountDataService,
|
private val accountDataService: AccountDataService,
|
||||||
|
@ -57,14 +62,12 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
|
|
||||||
override fun generateKey(keyId: String,
|
override fun generateKey(keyId: String,
|
||||||
keyName: String,
|
keyName: String,
|
||||||
keySigner: KeySigner,
|
keySigner: KeySigner?,
|
||||||
callback: MatrixCallback<SsssKeyCreationInfo>) {
|
callback: MatrixCallback<SsssKeyCreationInfo>) {
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
val key = try {
|
val key = try {
|
||||||
withOlmDecryption { olmPkDecryption ->
|
ByteArray(32).also {
|
||||||
val pubKey = olmPkDecryption.generateKey()
|
SecureRandom().nextBytes(it)
|
||||||
val privateKey = olmPkDecryption.privateKey()
|
|
||||||
Key(pubKey, privateKey)
|
|
||||||
}
|
}
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
|
@ -73,12 +76,11 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
|
|
||||||
val storageKeyContent = SecretStorageKeyContent(
|
val storageKeyContent = SecretStorageKeyContent(
|
||||||
name = keyName,
|
name = keyName,
|
||||||
algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
|
algorithm = SSSS_ALGORITHM_AES_HMAC_SHA2,
|
||||||
passphrase = null,
|
passphrase = null
|
||||||
publicKey = key.publicKey
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
|
val signedContent = keySigner?.sign(storageKeyContent.canonicalSignable())?.let {
|
||||||
storageKeyContent.copy(
|
storageKeyContent.copy(
|
||||||
signatures = it
|
signatures = it
|
||||||
)
|
)
|
||||||
|
@ -96,7 +98,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
callback.onSuccess(SsssKeyCreationInfo(
|
callback.onSuccess(SsssKeyCreationInfo(
|
||||||
keyId = keyId,
|
keyId = keyId,
|
||||||
content = storageKeyContent,
|
content = storageKeyContent,
|
||||||
recoveryKey = computeRecoveryKey(key.privateKey)
|
recoveryKey = computeRecoveryKey(key)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,19 +115,9 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
|
val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
|
||||||
|
|
||||||
val pubKey = try {
|
|
||||||
withOlmDecryption { olmPkDecryption ->
|
|
||||||
olmPkDecryption.setPrivateKey(privatePart.privateKey)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val storageKeyContent = SecretStorageKeyContent(
|
val storageKeyContent = SecretStorageKeyContent(
|
||||||
algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
|
algorithm = SSSS_ALGORITHM_AES_HMAC_SHA2,
|
||||||
passphrase = SsssPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt),
|
passphrase = SsssPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt)
|
||||||
publicKey = pubKey
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
|
val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
|
||||||
|
@ -188,24 +180,19 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
return getKey(keyId)
|
return getKey(keyId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) {
|
override fun storeSecret(name: String, secretBase64: String, keys: List<SharedSecretStorageService.KeyRef>, callback: MatrixCallback<Unit>) {
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
val encryptedContents = HashMap<String, EncryptedSecretContent>()
|
val encryptedContents = HashMap<String, EncryptedSecretContent>()
|
||||||
try {
|
try {
|
||||||
if (keys.isNullOrEmpty()) {
|
keys.forEach {
|
||||||
// use default key
|
val keyId = it.keyId
|
||||||
when (val key = getDefaultKey()) {
|
// encrypt the content
|
||||||
|
when (val key = keyId?.let { getKey(keyId) } ?: getDefaultKey()) {
|
||||||
is KeyInfoResult.Success -> {
|
is KeyInfoResult.Success -> {
|
||||||
if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_AES_HMAC_SHA2) {
|
||||||
val encryptedResult = withOlmEncryption { olmEncrypt ->
|
encryptAesHmacSha2(it.keySpec!!, name, secretBase64).let {
|
||||||
olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
|
encryptedContents[key.keyInfo.id] = it
|
||||||
olmEncrypt.encrypt(secretBase64)
|
|
||||||
}
|
}
|
||||||
encryptedContents[key.keyInfo.id] = EncryptedSecretContent(
|
|
||||||
ciphertext = encryptedResult.mCipherText,
|
|
||||||
ephemeral = encryptedResult.mEphemeralKey,
|
|
||||||
mac = encryptedResult.mMac
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Unknown algorithm
|
// Unknown algorithm
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
|
callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
|
||||||
|
@ -217,34 +204,6 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
keys.forEach {
|
|
||||||
val keyId = it
|
|
||||||
// encrypt the content
|
|
||||||
when (val key = getKey(keyId)) {
|
|
||||||
is KeyInfoResult.Success -> {
|
|
||||||
if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
|
||||||
val encryptedResult = withOlmEncryption { olmEncrypt ->
|
|
||||||
olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
|
|
||||||
olmEncrypt.encrypt(secretBase64)
|
|
||||||
}
|
|
||||||
encryptedContents[keyId] = EncryptedSecretContent(
|
|
||||||
ciphertext = encryptedResult.mCipherText,
|
|
||||||
ephemeral = encryptedResult.mEphemeralKey,
|
|
||||||
mac = encryptedResult.mMac
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Unknown algorithm
|
|
||||||
callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is KeyInfoResult.Error -> {
|
|
||||||
callback.onFailure(key.error)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accountDataService.updateAccountData(
|
accountDataService.updateAccountData(
|
||||||
|
@ -258,8 +217,109 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add default key
|
/**
|
||||||
|
* Encryption algorithm m.secret_storage.v1.aes-hmac-sha2
|
||||||
|
* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. The data is encrypted and MACed as follows:
|
||||||
|
*
|
||||||
|
* Given the secret storage key, generate 64 bytes by performing an HKDF with SHA-256 as the hash, a salt of 32 bytes
|
||||||
|
* of 0, and with the secret name as the info.
|
||||||
|
*
|
||||||
|
* The first 32 bytes are used as the AES key, and the next 32 bytes are used as the MAC key
|
||||||
|
*
|
||||||
|
* Generate 16 random bytes, set bit 63 to 0 (in order to work around differences in AES-CTR implementations), and use
|
||||||
|
* this as the AES initialization vector.
|
||||||
|
* This becomes the iv property, encoded using base64.
|
||||||
|
*
|
||||||
|
* Encrypt the data using AES-CTR-256 using the AES key generated above.
|
||||||
|
*
|
||||||
|
* This encrypted data, encoded using base64, becomes the ciphertext property.
|
||||||
|
*
|
||||||
|
* Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256 using the MAC key generated above.
|
||||||
|
* The resulting MAC is base64-encoded and becomes the mac property.
|
||||||
|
* (We use AES-CTR to match file encryption and key exports.)
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
private fun encryptAesHmacSha2(secretKey: SsssKeySpec, secretName: String, clearDataBase64: String): EncryptedSecretContent {
|
||||||
|
secretKey as RawBytesKeySpec
|
||||||
|
val pseudoRandomKey = HkdfSha256.deriveSecret(
|
||||||
|
secretKey.privateKey,
|
||||||
|
ByteArray(32) { 0.toByte() },
|
||||||
|
secretName.toByteArray(),
|
||||||
|
64)
|
||||||
|
|
||||||
|
// The first 32 bytes are used as the AES key, and the next 32 bytes are used as the MAC key
|
||||||
|
val aesKey = pseudoRandomKey.copyOfRange(0, 32)
|
||||||
|
val macKey = pseudoRandomKey.copyOfRange(32, 64)
|
||||||
|
|
||||||
|
val secureRandom = SecureRandom()
|
||||||
|
val iv = ByteArray(16)
|
||||||
|
secureRandom.nextBytes(iv)
|
||||||
|
|
||||||
|
// clear bit 63 of the salt to stop us hitting the 64-bit counter boundary
|
||||||
|
// (which would mean we wouldn't be able to decrypt on Android). The loss
|
||||||
|
// of a single bit of salt is a price we have to pay.
|
||||||
|
iv[9] = iv[9] and 0x7f
|
||||||
|
|
||||||
|
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
|
||||||
|
|
||||||
|
val secretKeySpec = SecretKeySpec(aesKey, "AES")
|
||||||
|
val ivParameterSpec = IvParameterSpec(iv)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
|
||||||
|
// secret are not that big, just do Final
|
||||||
|
val cipherBytes = cipher.doFinal(clearDataBase64.fromBase64())
|
||||||
|
require(cipherBytes.isNotEmpty())
|
||||||
|
|
||||||
|
val macKeySpec = SecretKeySpec(macKey, "HmacSHA256")
|
||||||
|
val mac = Mac.getInstance("HmacSHA256")
|
||||||
|
mac.init(macKeySpec)
|
||||||
|
val digest = mac.doFinal(cipherBytes)
|
||||||
|
|
||||||
|
return EncryptedSecretContent(
|
||||||
|
ciphertext = cipherBytes.toBase64NoPadding(),
|
||||||
|
initializationVector = iv.toBase64NoPadding(),
|
||||||
|
mac = digest.toBase64NoPadding()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decryptAesHmacSha2(secretKey: SsssKeySpec, secretName: String, cipherContent: EncryptedSecretContent): String {
|
||||||
|
secretKey as RawBytesKeySpec
|
||||||
|
val pseudoRandomKey = HkdfSha256.deriveSecret(
|
||||||
|
secretKey.privateKey,
|
||||||
|
ByteArray(32) { 0.toByte() },
|
||||||
|
secretName.toByteArray(),
|
||||||
|
64)
|
||||||
|
|
||||||
|
// The first 32 bytes are used as the AES key, and the next 32 bytes are used as the MAC key
|
||||||
|
val aesKey = pseudoRandomKey.copyOfRange(0, 32)
|
||||||
|
val macKey = pseudoRandomKey.copyOfRange(32, 64)
|
||||||
|
|
||||||
|
val iv = cipherContent.initializationVector?.fromBase64() ?: ByteArray(16)
|
||||||
|
|
||||||
|
val cipherRawBytes = cipherContent.ciphertext?.fromBase64() ?: throw SharedSecretStorageError.BadCipherText
|
||||||
|
|
||||||
|
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
|
||||||
|
|
||||||
|
val secretKeySpec = SecretKeySpec(aesKey, "AES")
|
||||||
|
val ivParameterSpec = IvParameterSpec(iv)
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
|
||||||
|
// secret are not that big, just do Final
|
||||||
|
val decryptedSecret = cipher.doFinal(cipherRawBytes)
|
||||||
|
|
||||||
|
require(decryptedSecret.isNotEmpty())
|
||||||
|
|
||||||
|
// Check Signature
|
||||||
|
val macKeySpec = SecretKeySpec(macKey, "HmacSHA256")
|
||||||
|
val mac = Mac.getInstance("HmacSHA256").apply { init(macKeySpec) }
|
||||||
|
val digest = mac.doFinal(cipherRawBytes)
|
||||||
|
|
||||||
|
if (!cipherContent.mac?.fromBase64()?.contentEquals(digest).orFalse()) {
|
||||||
|
throw SharedSecretStorageError.BadMac
|
||||||
|
} else {
|
||||||
|
// we are good
|
||||||
|
return decryptedSecret.toBase64NoPadding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAlgorithmsForSecret(name: String): List<KeyInfoResult> {
|
override fun getAlgorithmsForSecret(name: String): List<KeyInfoResult> {
|
||||||
|
@ -299,7 +359,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
|
|
||||||
val algorithm = key.keyInfo.content
|
val algorithm = key.keyInfo.content
|
||||||
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? Curve25519AesSha2KeySpec ?: return Unit.also {
|
val keySpec = secretKey as? RawBytesKeySpec ?: return Unit.also {
|
||||||
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
||||||
}
|
}
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
@ -317,6 +377,15 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
}
|
}
|
||||||
}.foldToCallback(callback)
|
}.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
|
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
||||||
|
val keySpec = secretKey as? RawBytesKeySpec ?: return Unit.also {
|
||||||
|
callback.onFailure(SharedSecretStorageError.BadKeyFormat)
|
||||||
|
}
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||||
|
kotlin.runCatching {
|
||||||
|
decryptAesHmacSha2(keySpec, name, secretContent)
|
||||||
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
callback.onFailure(SharedSecretStorageError.UnsupportedAlgorithm(algorithm.algorithm ?: ""))
|
callback.onFailure(SharedSecretStorageError.UnsupportedAlgorithm(algorithm.algorithm ?: ""))
|
||||||
}
|
}
|
||||||
|
@ -327,4 +396,37 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
const val ENCRYPTED = "encrypted"
|
const val ENCRYPTED = "encrypted"
|
||||||
const val DEFAULT_KEY_ID = "m.secret_storage.default_key"
|
const val DEFAULT_KEY_ID = "m.secret_storage.default_key"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkShouldBeAbleToAccessSecrets(secretNames: List<String>, keyId: String?): IntegrityResult {
|
||||||
|
if (secretNames.isEmpty()) {
|
||||||
|
return IntegrityResult.Error(SharedSecretStorageError.UnknownSecret("none"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyInfoResult = if (keyId == null) {
|
||||||
|
getDefaultKey()
|
||||||
|
} else {
|
||||||
|
getKey(keyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
||||||
|
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
||||||
|
|
||||||
|
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
|
|| keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
||||||
|
// Unsupported algorithm
|
||||||
|
return IntegrityResult.Error(
|
||||||
|
SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretNames.forEach { secretName ->
|
||||||
|
val secretEvent = accountDataService.getAccountDataEvent(secretName)
|
||||||
|
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownSecret(secretName))
|
||||||
|
if ((secretEvent.content["encrypted"] as? Map<*, *>)?.get(keyInfo.id) == null) {
|
||||||
|
return IntegrityResult.Error(SharedSecretStorageError.SecretNotEncryptedWithKey(secretName, keyInfo.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntegrityResult.Success(keyInfo.content.passphrase != null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ package im.vector.matrix.android.internal.crypto.store
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon
|
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||||
|
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon
|
||||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||||
|
@ -413,6 +413,8 @@ internal interface IMXCryptoStore {
|
||||||
fun getLiveCrossSigningInfo(userId: String) : LiveData<Optional<MXCrossSigningInfo>>
|
fun getLiveCrossSigningInfo(userId: String) : LiveData<Optional<MXCrossSigningInfo>>
|
||||||
fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
|
fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
|
||||||
|
|
||||||
|
fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean)
|
||||||
|
|
||||||
fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
|
fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
|
||||||
fun getCrossSigningPrivateKeys() : PrivateKeysInfo?
|
fun getCrossSigningPrivateKeys() : PrivateKeysInfo?
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.api.util.toOptional
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon
|
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||||
|
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon
|
||||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
||||||
|
@ -1094,6 +1094,23 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean) {
|
||||||
|
doRealmTransaction(realmConfiguration) { realm ->
|
||||||
|
realm.where<CryptoMetadataEntity>().findFirst()?.userId?.let { myUserId ->
|
||||||
|
CrossSigningInfoEntity.get(realm, myUserId)?.getMasterKey()?.let { xInfoEntity ->
|
||||||
|
val level = xInfoEntity.trustLevelEntity
|
||||||
|
if (level == null) {
|
||||||
|
val newLevel = realm.createObject(TrustLevelEntity::class.java)
|
||||||
|
newLevel.locallyVerified = trusted
|
||||||
|
xInfoEntity.trustLevelEntity = newLevel
|
||||||
|
} else {
|
||||||
|
level.locallyVerified = trusted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? {
|
private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? {
|
||||||
var existing = CrossSigningInfoEntity.get(realm, userId)
|
var existing = CrossSigningInfoEntity.get(realm, userId)
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
|
|
|
@ -16,7 +16,18 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db
|
package im.vector.matrix.android.internal.crypto.store.db
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.model.*
|
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity
|
||||||
|
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
|
||||||
import io.realm.annotations.RealmModule
|
import io.realm.annotations.RealmModule
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,18 +18,24 @@ package im.vector.matrix.android.internal.crypto.tasks
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageRelationContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationReadyContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationStartContent
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationService
|
||||||
import im.vector.matrix.android.internal.di.DeviceId
|
import im.vector.matrix.android.internal.di.DeviceId
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.ArrayList
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface RoomVerificationUpdateTask : Task<RoomVerificationUpdateTask.Params, Unit> {
|
internal interface RoomVerificationUpdateTask : Task<RoomVerificationUpdateTask.Params, Unit> {
|
||||||
|
@ -83,11 +89,14 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
||||||
}
|
}
|
||||||
Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} type: ${event.getClearType()}")
|
Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} type: ${event.getClearType()}")
|
||||||
|
|
||||||
|
// Relates to is not encrypted
|
||||||
|
val relatesToEventId = event.content.toModel<MessageRelationContent>()?.relatesTo?.eventId
|
||||||
|
|
||||||
if (event.senderId == userId) {
|
if (event.senderId == userId) {
|
||||||
// If it's send from me, we need to keep track of Requests or Start
|
// If it's send from me, we need to keep track of Requests or Start
|
||||||
// done from another device of mine
|
// done from another device of mine
|
||||||
|
|
||||||
if (EventType.MESSAGE == event.type) {
|
if (EventType.MESSAGE == event.getClearType()) {
|
||||||
val msgType = event.getClearContent().toModel<MessageContent>()?.msgType
|
val msgType = event.getClearContent().toModel<MessageContent>()?.msgType
|
||||||
if (MessageType.MSGTYPE_VERIFICATION_REQUEST == msgType) {
|
if (MessageType.MSGTYPE_VERIFICATION_REQUEST == msgType) {
|
||||||
event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
|
event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
|
||||||
|
@ -98,26 +107,26 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (EventType.KEY_VERIFICATION_START == event.type) {
|
} else if (EventType.KEY_VERIFICATION_START == event.getClearType()) {
|
||||||
event.getClearContent().toModel<MessageVerificationStartContent>()?.let {
|
event.getClearContent().toModel<MessageVerificationStartContent>()?.let {
|
||||||
if (it.fromDevice != deviceId) {
|
if (it.fromDevice != deviceId) {
|
||||||
// The verification is started from another device
|
// The verification is started from another device
|
||||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ")
|
||||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (EventType.KEY_VERIFICATION_READY == event.type) {
|
} else if (EventType.KEY_VERIFICATION_READY == event.getClearType()) {
|
||||||
event.getClearContent().toModel<MessageVerificationReadyContent>()?.let {
|
event.getClearContent().toModel<MessageVerificationReadyContent>()?.let {
|
||||||
if (it.fromDevice != deviceId) {
|
if (it.fromDevice != deviceId) {
|
||||||
// The verification is started from another device
|
// The verification is started from another device
|
||||||
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
|
Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ")
|
||||||
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (EventType.KEY_VERIFICATION_CANCEL == event.type || EventType.KEY_VERIFICATION_DONE == event.type) {
|
} else if (EventType.KEY_VERIFICATION_CANCEL == event.getClearType() || EventType.KEY_VERIFICATION_DONE == event.getClearType()) {
|
||||||
event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId?.let {
|
relatesToEventId?.let {
|
||||||
transactionsHandledByOtherDevice.remove(it)
|
transactionsHandledByOtherDevice.remove(it)
|
||||||
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
params.verificationService.onRoomRequestHandledByOtherDevice(event)
|
||||||
}
|
}
|
||||||
|
@ -127,10 +136,9 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
|
|
||||||
val relatesTo = event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId
|
if (relatesToEventId != null && transactionsHandledByOtherDevice.contains(relatesToEventId)) {
|
||||||
if (relatesTo != null && transactionsHandledByOtherDevice.contains(relatesTo)) {
|
|
||||||
// Ignore this event, it is directed to another of my devices
|
// Ignore this event, it is directed to another of my devices
|
||||||
Timber.v("## SAS Verification live observer: Ignore Transaction handled by other device tid:$relatesTo ")
|
Timber.v("## SAS Verification live observer: Ignore Transaction handled by other device tid:$relatesToEventId ")
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
when (event.getClearType()) {
|
when (event.getClearType()) {
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
* Copyright (C) 2015 Square, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package im.vector.matrix.android.internal.crypto.tools
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-based Extract-and-Expand Key Derivation Function (HkdfSha256)
|
||||||
|
* [RFC-5869] https://tools.ietf.org/html/rfc5869
|
||||||
|
*/
|
||||||
|
object HkdfSha256 {
|
||||||
|
|
||||||
|
public fun deriveSecret(inputKeyMaterial: ByteArray, salt: ByteArray?, info: ByteArray, outputLength: Int): ByteArray {
|
||||||
|
return expand(extract(salt, inputKeyMaterial), info, outputLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HkdfSha256-Extract(salt, IKM) -> PRK
|
||||||
|
*
|
||||||
|
* @param salt optional salt value (a non-secret random value);
|
||||||
|
* if not provided, it is set to a string of HashLen (size in octets) zeros.
|
||||||
|
* @param ikm input keying material
|
||||||
|
*/
|
||||||
|
private fun extract(salt: ByteArray?, ikm: ByteArray): ByteArray {
|
||||||
|
val mac = initMac(salt ?: ByteArray(HASH_LEN) { 0.toByte() })
|
||||||
|
return mac.doFinal(ikm)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HkdfSha256-Expand(PRK, info, L) -> OKM
|
||||||
|
*
|
||||||
|
* @param prk a pseudorandom key of at least HashLen bytes (usually, the output from the extract step)
|
||||||
|
* @param info optional context and application specific information (can be empty)
|
||||||
|
* @param outputLength length of output keying material in bytes (<= 255*HashLen)
|
||||||
|
* @return OKM output keying material
|
||||||
|
*/
|
||||||
|
private fun expand(prk: ByteArray, info: ByteArray = ByteArray(0), outputLength: Int): ByteArray {
|
||||||
|
require(outputLength <= 255 * HASH_LEN) { "outputLength must be less than or equal to 255*HashLen" }
|
||||||
|
|
||||||
|
/*
|
||||||
|
The output OKM is calculated as follows:
|
||||||
|
Notation | -> When the message is composed of several elements we use concatenation (denoted |) in the second argument;
|
||||||
|
|
||||||
|
|
||||||
|
N = ceil(L/HashLen)
|
||||||
|
T = T(1) | T(2) | T(3) | ... | T(N)
|
||||||
|
OKM = first L octets of T
|
||||||
|
|
||||||
|
where:
|
||||||
|
T(0) = empty string (zero length)
|
||||||
|
T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
||||||
|
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
||||||
|
T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
val n = ceil(outputLength.toDouble() / HASH_LEN.toDouble()).toInt()
|
||||||
|
|
||||||
|
var stepHash = ByteArray(0) // T(0) empty string (zero length)
|
||||||
|
|
||||||
|
val generatedBytes = ByteArrayOutputStream() // ByteBuffer.allocate(Math.multiplyExact(n, HASH_LEN))
|
||||||
|
val mac = initMac(prk)
|
||||||
|
for (roundNum in 1..n) {
|
||||||
|
mac.reset()
|
||||||
|
val t = ByteBuffer.allocate(stepHash.size + info.size + 1).apply {
|
||||||
|
put(stepHash)
|
||||||
|
put(info)
|
||||||
|
put(roundNum.toByte())
|
||||||
|
}
|
||||||
|
stepHash = mac.doFinal(t.array())
|
||||||
|
generatedBytes.write(stepHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generatedBytes.toByteArray().sliceArray(0 until outputLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initMac(secret: ByteArray): Mac {
|
||||||
|
val mac = Mac.getInstance(HASH_ALG)
|
||||||
|
mac.init(SecretKeySpec(secret, HASH_ALG))
|
||||||
|
return mac
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val HASH_LEN = 32
|
||||||
|
private const val HASH_ALG = "HmacSHA256"
|
||||||
|
}
|
|
@ -18,10 +18,10 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
import im.vector.matrix.android.api.session.crypto.verification.SasMode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
|
|
|
@ -22,14 +22,14 @@ import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.QrCodeVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.QrCodeVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.SasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
|
import im.vector.matrix.android.api.session.crypto.verification.safeValueOf
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||||
|
@ -255,7 +255,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRoomRequestHandledByOtherDevice(event: Event) {
|
fun onRoomRequestHandledByOtherDevice(event: Event) {
|
||||||
val requestInfo = event.getClearContent().toModel<MessageRelationContent>()
|
val requestInfo = event.content.toModel<MessageRelationContent>()
|
||||||
?: return
|
?: return
|
||||||
val requestId = requestInfo.relatesTo?.eventId ?: return
|
val requestId = requestInfo.relatesTo?.eventId ?: return
|
||||||
getExistingVerificationRequestInRoom(event.roomId ?: "", requestId)?.let {
|
getExistingVerificationRequestInRoom(event.roomId ?: "", requestId)?.let {
|
||||||
|
@ -465,7 +465,11 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
||||||
// If there is a corresponding request, we can auto accept
|
// If there is a corresponding request, we can auto accept
|
||||||
// as we are the one requesting in first place (or we accepted the request)
|
// as we are the one requesting in first place (or we accepted the request)
|
||||||
val autoAccept = getExistingVerificationRequest(otherUserId)?.any { it.transactionId == startReq.transactionID }
|
// I need to check if the pending request was related to this device also
|
||||||
|
val autoAccept = getExistingVerificationRequest(otherUserId)?.any {
|
||||||
|
it.transactionId == startReq.transactionID
|
||||||
|
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
||||||
|
}
|
||||||
?: false
|
?: false
|
||||||
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// this,
|
// this,
|
||||||
|
@ -1083,8 +1087,12 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
.distinct()
|
.distinct()
|
||||||
|
|
||||||
transport.sendVerificationRequest(methodValues, localID, otherUserId, null, targetDevices) { _, _ ->
|
transport.sendVerificationRequest(methodValues, localID, otherUserId, null, targetDevices) { _, info ->
|
||||||
// Nothing special to do in to device mode
|
// Nothing special to do in to device mode
|
||||||
|
updatePendingRequest(verificationRequest.copy(
|
||||||
|
// localId stays different
|
||||||
|
requestInfo = info
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestsForUser.add(verificationRequest)
|
requestsForUser.add(verificationRequest)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic interactive key verification transaction
|
* Generic interactive key verification transaction
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.extensions.orFalse
|
import im.vector.matrix.android.api.extensions.orFalse
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||||
|
|
|
@ -18,11 +18,11 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasMode
|
import im.vector.matrix.android.api.session.crypto.verification.SasMode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.SasVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
||||||
|
@ -299,20 +299,27 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not me sign his MSK and upload the signature
|
// If not me sign his MSK and upload the signature
|
||||||
if (otherMasterKeyIsVerified && otherUserId != userId) {
|
if (otherMasterKeyIsVerified) {
|
||||||
// we should trust this master key
|
// we should trust this master key
|
||||||
// And check verification MSK -> SSK?
|
// And check verification MSK -> SSK?
|
||||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
if (otherUserId != userId) {
|
||||||
override fun onFailure(failure: Throwable) {
|
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
||||||
Timber.e(failure, "## SAS Verification: Failed to trust User $otherUserId")
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.e(failure, "## SAS Verification: Failed to trust User $otherUserId")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Notice other master key is mine because other is me
|
||||||
|
if (otherMasterKey?.trustLevel?.isVerified() == false) {
|
||||||
|
crossSigningService.markMyMasterKeyAsTrusted()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherUserId == userId) {
|
if (otherUserId == userId) {
|
||||||
// If me it's reasonable to sign and upload the device signature
|
// If me it's reasonable to sign and upload the device signature
|
||||||
// Notice that i might not have the private keys, so may not be able to do it
|
// Notice that i might not have the private keys, so may not be able to do it
|
||||||
crossSigningService.signDevice(otherDeviceId!!, object : MatrixCallback<Unit> {
|
crossSigningService.trustDevice(otherDeviceId!!, object : MatrixCallback<Unit> {
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
Timber.w(failure, "## SAS Verification: Failed to sign new device $otherDeviceId")
|
Timber.w(failure, "## SAS Verification: Failed to sign new device $otherDeviceId")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.R
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.EmojiRepresentation
|
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
|
||||||
|
|
||||||
internal fun getEmojiForCode(code: Int): EmojiRepresentation {
|
internal fun getEmojiForCode(code: Int): EmojiRepresentation {
|
||||||
return when (code % 64) {
|
return when (code % 64) {
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verification can be performed using toDevice events or via DM.
|
* Verification can be performed using toDevice events or via DM.
|
||||||
|
|
|
@ -23,8 +23,8 @@ import androidx.work.Operation
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.R
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
package im.vector.matrix.android.internal.crypto.verification
|
package im.vector.matrix.android.internal.crypto.verification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
|
|
|
@ -18,12 +18,14 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.QrCodeVerificationTransaction
|
import im.vector.matrix.android.api.session.crypto.verification.QrCodeVerificationTransaction
|
||||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
||||||
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||||
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64Safe
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction
|
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction
|
||||||
import im.vector.matrix.android.internal.crypto.verification.VerificationInfo
|
import im.vector.matrix.android.internal.crypto.verification.VerificationInfo
|
||||||
|
@ -199,7 +201,7 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startReq.sharedSecret == qrCodeData.sharedSecret) {
|
if (startReq.sharedSecret?.fromBase64Safe()?.contentEquals(qrCodeData.sharedSecret.fromBase64()) == true) {
|
||||||
// Ok, we can trust the other user
|
// Ok, we can trust the other user
|
||||||
// We can only trust the master key in this case
|
// We can only trust the master key in this case
|
||||||
// But first, ask the user for a confirmation
|
// But first, ask the user for a confirmation
|
||||||
|
@ -222,20 +224,25 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||||
|
|
||||||
private fun trust(canTrustOtherUserMasterKey: Boolean, toVerifyDeviceIds: List<String>) {
|
private fun trust(canTrustOtherUserMasterKey: Boolean, toVerifyDeviceIds: List<String>) {
|
||||||
// If not me sign his MSK and upload the signature
|
// If not me sign his MSK and upload the signature
|
||||||
if (otherUserId != userId && canTrustOtherUserMasterKey) {
|
if (canTrustOtherUserMasterKey) {
|
||||||
// we should trust this master key
|
if (otherUserId != userId) {
|
||||||
// And check verification MSK -> SSK?
|
// we should trust this master key
|
||||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
// And check verification MSK -> SSK?
|
||||||
override fun onFailure(failure: Throwable) {
|
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
|
||||||
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
override fun onFailure(failure: Throwable) {
|
||||||
}
|
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Mark my keys as trusted locally
|
||||||
|
crossSigningService.markMyMasterKeyAsTrusted()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherUserId == userId) {
|
if (otherUserId == userId) {
|
||||||
// If me it's reasonable to sign and upload the device signature
|
// If me it's reasonable to sign and upload the device signature
|
||||||
// Notice that i might not have the private keys, so may not be able to do it
|
// Notice that i might not have the private keys, so may not be able to do it
|
||||||
crossSigningService.signDevice(otherDeviceId!!, object : MatrixCallback<Unit> {
|
crossSigningService.trustDevice(otherDeviceId!!, object : MatrixCallback<Unit> {
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
Timber.w(failure, "## QR Verification: Failed to sign new device $otherDeviceId")
|
Timber.w(failure, "## QR Verification: Failed to sign new device $otherDeviceId")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.verification.qrcode
|
package im.vector.matrix.android.internal.crypto.verification.qrcode
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64NoPadding
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import im.vector.matrix.android.internal.extensions.toUnsignedInt
|
import im.vector.matrix.android.internal.extensions.toUnsignedInt
|
||||||
|
|
||||||
|
@ -52,15 +52,15 @@ fun QrCodeData.toEncodedString(): String {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
firstKey.fromBase64NoPadding().forEach {
|
firstKey.fromBase64().forEach {
|
||||||
result += it
|
result += it
|
||||||
}
|
}
|
||||||
secondKey.fromBase64NoPadding().forEach {
|
secondKey.fromBase64().forEach {
|
||||||
result += it
|
result += it
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secret
|
// Secret
|
||||||
sharedSecret.fromBase64NoPadding().forEach {
|
sharedSecret.fromBase64().forEach {
|
||||||
result += it
|
result += it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,11 +94,11 @@ fun String.toQrCodeData(): QrCodeData? {
|
||||||
val mode = byteArray[cursor].toInt()
|
val mode = byteArray[cursor].toInt()
|
||||||
cursor++
|
cursor++
|
||||||
|
|
||||||
// Get transaction length
|
// Get transaction length, Big Endian format
|
||||||
val bigEndian1 = byteArray[cursor].toUnsignedInt()
|
val msb = byteArray[cursor].toUnsignedInt()
|
||||||
val bigEndian2 = byteArray[cursor + 1].toUnsignedInt()
|
val lsb = byteArray[cursor + 1].toUnsignedInt()
|
||||||
|
|
||||||
val transactionLength = bigEndian1 * 0x0100 + bigEndian2
|
val transactionLength = msb.shl(8) + lsb
|
||||||
|
|
||||||
cursor++
|
cursor++
|
||||||
cursor++
|
cursor++
|
||||||
|
|
|
@ -18,7 +18,11 @@ package im.vector.matrix.android.internal.database
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
||||||
import io.realm.*
|
import io.realm.OrderedRealmCollectionChangeListener
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.RealmResults
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
|
|
|
@ -16,8 +16,16 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.database
|
package im.vector.matrix.android.internal.database
|
||||||
|
|
||||||
import io.realm.*
|
import io.realm.Realm
|
||||||
import kotlinx.coroutines.*
|
import io.realm.RealmChangeListener
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.RealmQuery
|
||||||
|
import io.realm.RealmResults
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
|
||||||
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
|
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
|
||||||
timeoutMillis: Long,
|
timeoutMillis: Long,
|
||||||
|
|
|
@ -16,19 +16,12 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.database.mapper
|
package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryMapper @Inject constructor(
|
internal class RoomSummaryMapper @Inject constructor(private val timelineEventMapper: TimelineEventMapper) {
|
||||||
private val cryptoService: CryptoService,
|
|
||||||
private val timelineEventMapper: TimelineEventMapper
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
||||||
val tags = roomSummaryEntity.tags.map {
|
val tags = roomSummaryEntity.tags.map {
|
||||||
|
@ -38,21 +31,6 @@ internal class RoomSummaryMapper @Inject constructor(
|
||||||
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
val latestEvent = roomSummaryEntity.latestPreviewableEvent?.let {
|
||||||
timelineEventMapper.map(it, buildReadReceipts = false)
|
timelineEventMapper.map(it, buildReadReceipts = false)
|
||||||
}
|
}
|
||||||
if (latestEvent?.root?.isEncrypted() == true && latestEvent.root.mxDecryptionResult == null) {
|
|
||||||
// TODO use a global event decryptor? attache to session and that listen to new sessionId?
|
|
||||||
// for now decrypt sync
|
|
||||||
try {
|
|
||||||
val result = cryptoService.decryptEvent(latestEvent.root, latestEvent.root.roomId + UUID.randomUUID().toString())
|
|
||||||
latestEvent.root.mxDecryptionResult = OlmDecryptionResult(
|
|
||||||
payload = result.clearEvent,
|
|
||||||
senderKey = result.senderCurve25519Key,
|
|
||||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
|
||||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
|
||||||
)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Timber.d(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RoomSummary(
|
return RoomSummary(
|
||||||
roomId = roomSummaryEntity.roomId,
|
roomId = roomSummaryEntity.roomId,
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
package im.vector.matrix.android.internal.database.query
|
package im.vector.matrix.android.internal.database.query
|
||||||
|
|
||||||
import im.vector.matrix.android.api.pushrules.RuleKind
|
import im.vector.matrix.android.api.pushrules.RuleKind
|
||||||
import im.vector.matrix.android.internal.database.model.*
|
|
||||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.PushRuleEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.PushRulesEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.extensions
|
package im.vector.matrix.android.internal.extensions
|
||||||
|
|
||||||
import arrow.core.*
|
import arrow.core.Failure
|
||||||
|
import arrow.core.Success
|
||||||
|
import arrow.core.Try
|
||||||
|
import arrow.core.TryOf
|
||||||
|
import arrow.core.fix
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
|
||||||
inline fun <A> TryOf<A>.onError(f: (Throwable) -> Unit): Try<A> = fix()
|
inline fun <A> TryOf<A>.onError(f: (Throwable) -> Unit): Try<A> = fix()
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.content.IntentFilter
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface NetworkCallbackStrategy {
|
internal interface NetworkCallbackStrategy {
|
||||||
|
@ -70,7 +71,16 @@ internal class PreferredNetworkCallbackStrategy @Inject constructor(context: Con
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unregister() {
|
override fun unregister() {
|
||||||
|
// It can crash after an application update, if not registered
|
||||||
|
val doUnregister = hasChangedCallback != null
|
||||||
hasChangedCallback = null
|
hasChangedCallback = null
|
||||||
conn.unregisterNetworkCallback(networkCallback)
|
if (doUnregister) {
|
||||||
|
// Add a try catch for safety
|
||||||
|
try {
|
||||||
|
conn.unregisterNetworkCallback(networkCallback)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Timber.e(t, "Unable to unregister network callback")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,11 @@ package im.vector.matrix.android.internal.network
|
||||||
|
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import okio.*
|
import okio.Buffer
|
||||||
|
import okio.BufferedSink
|
||||||
|
import okio.ForwardingSink
|
||||||
|
import okio.Sink
|
||||||
|
import okio.buffer
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
internal class ProgressRequestBody(private val delegate: RequestBody,
|
internal class ProgressRequestBody(private val delegate: RequestBody,
|
||||||
|
|
|
@ -24,7 +24,14 @@ import java.security.KeyStore
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.HostnameVerifier
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException
|
||||||
|
import javax.net.ssl.SSLSocketFactory
|
||||||
|
import javax.net.ssl.TrustManager
|
||||||
|
import javax.net.ssl.TrustManagerFactory
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,6 +49,7 @@ import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.di.SessionId
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecryptor
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
import im.vector.matrix.android.internal.session.sync.job.SyncThread
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||||
|
@ -93,6 +94,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
||||||
private val accountDataService: Lazy<AccountDataService>,
|
private val accountDataService: Lazy<AccountDataService>,
|
||||||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||||
|
private val timelineEventDecryptor: TimelineEventDecryptor,
|
||||||
private val shieldTrustUpdater: ShieldTrustUpdater)
|
private val shieldTrustUpdater: ShieldTrustUpdater)
|
||||||
: Session,
|
: Session,
|
||||||
RoomService by roomService.get(),
|
RoomService by roomService.get(),
|
||||||
|
@ -126,6 +128,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
isOpen = true
|
isOpen = true
|
||||||
liveEntityObservers.forEach { it.start() }
|
liveEntityObservers.forEach { it.start() }
|
||||||
eventBus.register(this)
|
eventBus.register(this)
|
||||||
|
timelineEventDecryptor.start()
|
||||||
shieldTrustUpdater.start()
|
shieldTrustUpdater.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +166,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
override fun close() {
|
override fun close() {
|
||||||
assert(isOpen)
|
assert(isOpen)
|
||||||
stopSync()
|
stopSync()
|
||||||
|
timelineEventDecryptor.destroy()
|
||||||
liveEntityObservers.forEach { it.dispose() }
|
liveEntityObservers.forEach { it.dispose() }
|
||||||
cryptoService.get().close()
|
cryptoService.get().close()
|
||||||
isOpen = false
|
isOpen = false
|
||||||
|
|
|
@ -46,7 +46,7 @@ internal object ThumbnailExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractVideoThumbnail(attachment: ContentAttachmentData): ThumbnailData? {
|
private fun extractVideoThumbnail(attachment: ContentAttachmentData): ThumbnailData? {
|
||||||
val thumbnail = ThumbnailUtils.createVideoThumbnail(attachment.path, MediaStore.Video.Thumbnails.MINI_KIND)
|
val thumbnail = ThumbnailUtils.createVideoThumbnail(attachment.path, MediaStore.Video.Thumbnails.MINI_KIND) ?: return null
|
||||||
val outputStream = ByteArrayOutputStream()
|
val outputStream = ByteArrayOutputStream()
|
||||||
thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
||||||
val thumbnailWidth = thumbnail.width
|
val thumbnailWidth = thumbnail.width
|
||||||
|
|
|
@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface GetHomeServerCapabilitiesTask : Task<Unit, Unit>
|
internal interface GetHomeServerCapabilitiesTask : Task<Unit, Unit>
|
||||||
|
|
|
@ -19,7 +19,11 @@ import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.DELETE
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
internal interface PushRulesApi {
|
internal interface PushRulesApi {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.internal.session.room
|
package im.vector.matrix.android.internal.session.room
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import dagger.Lazy
|
||||||
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
@ -41,17 +42,20 @@ import im.vector.matrix.android.internal.database.query.whereType
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
|
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
|
||||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||||
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventDecryptor
|
||||||
import im.vector.matrix.android.internal.session.sync.RoomSyncHandler
|
import im.vector.matrix.android.internal.session.sync.RoomSyncHandler
|
||||||
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
|
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
|
||||||
import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications
|
import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSummaryUpdater @Inject constructor(
|
internal class RoomSummaryUpdater @Inject constructor(
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val roomDisplayNameResolver: RoomDisplayNameResolver,
|
private val roomDisplayNameResolver: RoomDisplayNameResolver,
|
||||||
private val roomAvatarResolver: RoomAvatarResolver,
|
private val roomAvatarResolver: RoomAvatarResolver,
|
||||||
|
private val timelineEventDecryptor: Lazy<TimelineEventDecryptor>,
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
private val monarchy: Monarchy) {
|
private val monarchy: Monarchy) {
|
||||||
|
|
||||||
|
@ -141,6 +145,11 @@ internal class RoomSummaryUpdater @Inject constructor(
|
||||||
roomSummaryEntity.inviterId = null
|
roomSummaryEntity.inviterId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (latestPreviewableEvent?.root?.type == EventType.ENCRYPTED && latestPreviewableEvent.root?.decryptionResultJson == null) {
|
||||||
|
Timber.v("Should decrypt ${latestPreviewableEvent.eventId}")
|
||||||
|
timelineEventDecryptor.get().requestDecryption(TimelineEventDecryptor.DecryptionRequest(latestPreviewableEvent.eventId, ""))
|
||||||
|
}
|
||||||
|
|
||||||
if (updateMembers) {
|
if (updateMembers) {
|
||||||
val otherRoomMembers = RoomMemberHelper(realm, roomId)
|
val otherRoomMembers = RoomMemberHelper(realm, roomId)
|
||||||
.queryRoomMembersEvent()
|
.queryRoomMembersEvent()
|
||||||
|
|
|
@ -27,8 +27,8 @@ import im.vector.matrix.android.internal.database.awaitTransaction
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.whereTypes
|
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.database.query.whereTypes
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
import io.realm.OrderedCollectionChangeSet
|
import io.realm.OrderedCollectionChangeSet
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
|
|
|
@ -16,9 +16,13 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.notification
|
package im.vector.matrix.android.internal.session.room.notification
|
||||||
|
|
||||||
import im.vector.matrix.android.api.pushrules.*
|
import im.vector.matrix.android.api.pushrules.Action
|
||||||
|
import im.vector.matrix.android.api.pushrules.Condition
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleSetKey
|
||||||
|
import im.vector.matrix.android.api.pushrules.getActions
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushCondition
|
import im.vector.matrix.android.api.pushrules.rest.PushCondition
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
|
import im.vector.matrix.android.api.pushrules.toJson
|
||||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
||||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.send
|
package im.vector.matrix.android.internal.session.room.send
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import im.vector.matrix.android.R
|
import im.vector.matrix.android.R
|
||||||
|
@ -275,9 +276,9 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
mediaDataRetriever.setDataSource(attachment.path)
|
mediaDataRetriever.setDataSource(attachment.path)
|
||||||
|
|
||||||
// Use frame to calculate height and width as we are sure to get the right ones
|
// Use frame to calculate height and width as we are sure to get the right ones
|
||||||
val firstFrame = mediaDataRetriever.frameAtTime
|
val firstFrame: Bitmap? = mediaDataRetriever.frameAtTime
|
||||||
val height = firstFrame.height
|
val height = firstFrame?.height ?: 0
|
||||||
val width = firstFrame.width
|
val width = firstFrame?.width ?: 0
|
||||||
mediaDataRetriever.release()
|
mediaDataRetriever.release()
|
||||||
|
|
||||||
val thumbnailInfo = ThumbnailExtractor.extractThumbnail(attachment)?.let {
|
val thumbnailInfo = ThumbnailExtractor.extractThumbnail(attachment)?.let {
|
||||||
|
|
|
@ -40,7 +40,6 @@ import im.vector.matrix.android.internal.util.awaitTransaction
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.IllegalStateException
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class LocalEchoRepository @Inject constructor(private val monarchy: Monarchy,
|
internal class LocalEchoRepository @Inject constructor(private val monarchy: Monarchy,
|
||||||
|
|
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.session.room.send.pills
|
||||||
|
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import im.vector.matrix.android.api.session.room.send.MatrixItemSpan
|
import im.vector.matrix.android.api.session.room.send.MatrixItemSpan
|
||||||
import java.util.*
|
import java.util.Collections
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.RelationType
|
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
@ -73,11 +72,11 @@ internal class DefaultTimeline(
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val contextOfEventTask: GetContextOfEventTask,
|
private val contextOfEventTask: GetContextOfEventTask,
|
||||||
private val paginationTask: PaginationTask,
|
private val paginationTask: PaginationTask,
|
||||||
private val cryptoService: CryptoService,
|
|
||||||
private val timelineEventMapper: TimelineEventMapper,
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
private val settings: TimelineSettings,
|
private val settings: TimelineSettings,
|
||||||
private val hiddenReadReceipts: TimelineHiddenReadReceipts,
|
private val hiddenReadReceipts: TimelineHiddenReadReceipts,
|
||||||
private val eventBus: EventBus
|
private val eventBus: EventBus,
|
||||||
|
private val eventDecryptor: TimelineEventDecryptor
|
||||||
) : Timeline, TimelineHiddenReadReceipts.Delegate {
|
) : Timeline, TimelineHiddenReadReceipts.Delegate {
|
||||||
|
|
||||||
data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
|
data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
|
||||||
|
@ -114,8 +113,6 @@ internal class DefaultTimeline(
|
||||||
override val isLive
|
override val isLive
|
||||||
get() = !hasMoreToLoad(Timeline.Direction.FORWARDS)
|
get() = !hasMoreToLoad(Timeline.Direction.FORWARDS)
|
||||||
|
|
||||||
private val eventDecryptor = TimelineEventDecryptor(realmConfiguration, timelineID, cryptoService)
|
|
||||||
|
|
||||||
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
||||||
if (!results.isLoaded || !results.isValid) {
|
if (!results.isLoaded || !results.isValid) {
|
||||||
return@OrderedRealmCollectionChangeListener
|
return@OrderedRealmCollectionChangeListener
|
||||||
|
@ -607,7 +604,7 @@ internal class DefaultTimeline(
|
||||||
|
|
||||||
if (timelineEvent.isEncrypted()
|
if (timelineEvent.isEncrypted()
|
||||||
&& timelineEvent.root.mxDecryptionResult == null) {
|
&& timelineEvent.root.mxDecryptionResult == null) {
|
||||||
timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) }
|
timelineEvent.root.eventId?.also { eventDecryptor.requestDecryption(TimelineEventDecryptor.DecryptionRequest(it, timelineID)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size
|
val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size
|
||||||
|
|
|
@ -21,7 +21,6 @@ import androidx.lifecycle.Transformations
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||||
|
@ -41,7 +40,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
|
||||||
private val eventBus: EventBus,
|
private val eventBus: EventBus,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val contextOfEventTask: GetContextOfEventTask,
|
private val contextOfEventTask: GetContextOfEventTask,
|
||||||
private val cryptoService: CryptoService,
|
private val eventDecryptor: TimelineEventDecryptor,
|
||||||
private val paginationTask: PaginationTask,
|
private val paginationTask: PaginationTask,
|
||||||
private val timelineEventMapper: TimelineEventMapper,
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper
|
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper
|
||||||
|
@ -60,11 +59,11 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
|
||||||
taskExecutor = taskExecutor,
|
taskExecutor = taskExecutor,
|
||||||
contextOfEventTask = contextOfEventTask,
|
contextOfEventTask = contextOfEventTask,
|
||||||
paginationTask = paginationTask,
|
paginationTask = paginationTask,
|
||||||
cryptoService = cryptoService,
|
|
||||||
timelineEventMapper = timelineEventMapper,
|
timelineEventMapper = timelineEventMapper,
|
||||||
settings = settings,
|
settings = settings,
|
||||||
hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
|
hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
|
||||||
eventBus = eventBus
|
eventBus = eventBus,
|
||||||
|
eventDecryptor = eventDecryptor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,19 @@ import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventConten
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class TimelineEventDecryptor(
|
@SessionScope
|
||||||
|
internal class TimelineEventDecryptor @Inject constructor(
|
||||||
|
@SessionDatabase
|
||||||
private val realmConfiguration: RealmConfiguration,
|
private val realmConfiguration: RealmConfiguration,
|
||||||
private val timelineId: String,
|
|
||||||
private val cryptoService: CryptoService
|
private val cryptoService: CryptoService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -53,9 +57,9 @@ internal class TimelineEventDecryptor(
|
||||||
private var executor: ExecutorService? = null
|
private var executor: ExecutorService? = null
|
||||||
|
|
||||||
// Set of eventIds which are currently decrypting
|
// Set of eventIds which are currently decrypting
|
||||||
private val existingRequests = mutableSetOf<String>()
|
private val existingRequests = mutableSetOf<DecryptionRequest>()
|
||||||
// sessionId -> list of eventIds
|
// sessionId -> list of eventIds
|
||||||
private val unknownSessionsFailure = mutableMapOf<String, MutableSet<String>>()
|
private val unknownSessionsFailure = mutableMapOf<String, MutableSet<DecryptionRequest>>()
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
executor = Executors.newSingleThreadExecutor()
|
executor = Executors.newSingleThreadExecutor()
|
||||||
|
@ -74,53 +78,51 @@ internal class TimelineEventDecryptor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestDecryption(eventId: String) {
|
fun requestDecryption(request: DecryptionRequest) {
|
||||||
synchronized(unknownSessionsFailure) {
|
synchronized(unknownSessionsFailure) {
|
||||||
for (eventIds in unknownSessionsFailure.values) {
|
for (requests in unknownSessionsFailure.values) {
|
||||||
if (eventId in eventIds) {
|
if (request in requests) {
|
||||||
Timber.d("Skip Decryption request for event $eventId, unknown session")
|
Timber.d("Skip Decryption request for event ${request.eventId}, unknown session")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized(existingRequests) {
|
synchronized(existingRequests) {
|
||||||
if (!existingRequests.add(eventId)) {
|
if (!existingRequests.add(request)) {
|
||||||
Timber.d("Skip Decryption request for event $eventId, already requested")
|
Timber.d("Skip Decryption request for event ${request.eventId}, already requested")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
executor?.execute {
|
executor?.execute {
|
||||||
Realm.getInstance(realmConfiguration).use { realm ->
|
Realm.getInstance(realmConfiguration).use { realm ->
|
||||||
processDecryptRequest(eventId, realm)
|
processDecryptRequest(request, realm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processDecryptRequest(eventId: String, realm: Realm) {
|
private fun processDecryptRequest(request: DecryptionRequest, realm: Realm) = realm.executeTransaction {
|
||||||
|
val eventId = request.eventId
|
||||||
|
val timelineId = request.timelineId
|
||||||
Timber.v("Decryption request for event $eventId")
|
Timber.v("Decryption request for event $eventId")
|
||||||
val eventEntity = EventEntity.where(realm, eventId = eventId).findFirst()
|
val eventEntity = EventEntity.where(realm, eventId = eventId).findFirst()
|
||||||
?: return Unit.also {
|
?: return@executeTransaction Unit.also {
|
||||||
Timber.d("Decryption request for unknown message")
|
Timber.d("Decryption request for unknown message")
|
||||||
}
|
}
|
||||||
val event = eventEntity.asDomain()
|
val event = eventEntity.asDomain()
|
||||||
try {
|
try {
|
||||||
val result = cryptoService.decryptEvent(event, timelineId)
|
val result = cryptoService.decryptEvent(event, timelineId)
|
||||||
Timber.v("Successfully decrypted event $eventId")
|
Timber.v("Successfully decrypted event $eventId")
|
||||||
realm.executeTransaction {
|
eventEntity.setDecryptionResult(result)
|
||||||
eventEntity.setDecryptionResult(result)
|
|
||||||
}
|
|
||||||
} catch (e: MXCryptoError) {
|
} catch (e: MXCryptoError) {
|
||||||
Timber.w(e, "Failed to decrypt event $eventId")
|
Timber.w(e, "Failed to decrypt event $eventId")
|
||||||
if (e is MXCryptoError.Base && e.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
if (e is MXCryptoError.Base && e.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
||||||
// Keep track of unknown sessions to automatically try to decrypt on new session
|
// Keep track of unknown sessions to automatically try to decrypt on new session
|
||||||
realm.executeTransaction {
|
eventEntity.decryptionErrorCode = e.errorType.name
|
||||||
eventEntity.decryptionErrorCode = e.errorType.name
|
|
||||||
}
|
|
||||||
event.content?.toModel<EncryptedEventContent>()?.let { content ->
|
event.content?.toModel<EncryptedEventContent>()?.let { content ->
|
||||||
content.sessionId?.let { sessionId ->
|
content.sessionId?.let { sessionId ->
|
||||||
synchronized(unknownSessionsFailure) {
|
synchronized(unknownSessionsFailure) {
|
||||||
val list = unknownSessionsFailure.getOrPut(sessionId) { mutableSetOf() }
|
val list = unknownSessionsFailure.getOrPut(sessionId) { mutableSetOf() }
|
||||||
list.add(eventId)
|
list.add(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,8 +131,13 @@ internal class TimelineEventDecryptor(
|
||||||
Timber.e(t, "Failed to decrypt event $eventId")
|
Timber.e(t, "Failed to decrypt event $eventId")
|
||||||
} finally {
|
} finally {
|
||||||
synchronized(existingRequests) {
|
synchronized(existingRequests) {
|
||||||
existingRequests.remove(eventId)
|
existingRequests.remove(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DecryptionRequest(
|
||||||
|
val eventId: String,
|
||||||
|
val timelineId: String
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
import androidx.work.*
|
import androidx.work.BackoffPolicy
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.ListenableWorker
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
|
|
|
@ -27,8 +27,8 @@ import im.vector.matrix.android.internal.database.awaitTransaction
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.whereTypes
|
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.database.query.whereTypes
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
import io.realm.OrderedCollectionChangeSet
|
import io.realm.OrderedCollectionChangeSet
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
|
|
|
@ -25,14 +25,25 @@ import android.security.keystore.KeyGenParameterSpec
|
||||||
import android.security.keystore.KeyProperties
|
import android.security.keystore.KeyProperties
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.*
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import javax.crypto.*
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.CipherInputStream
|
||||||
|
import javax.crypto.CipherOutputStream
|
||||||
|
import javax.crypto.KeyGenerator
|
||||||
|
import javax.crypto.SecretKey
|
||||||
|
import javax.crypto.SecretKeyFactory
|
||||||
import javax.crypto.spec.GCMParameterSpec
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.PBEKeySpec
|
import javax.crypto.spec.PBEKeySpec
|
||||||
|
|
|
@ -23,7 +23,13 @@ import im.vector.matrix.android.internal.SessionManager
|
||||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoModule
|
import im.vector.matrix.android.internal.crypto.CryptoModule
|
||||||
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||||
import im.vector.matrix.android.internal.di.*
|
import im.vector.matrix.android.internal.di.CryptoDatabase
|
||||||
|
import im.vector.matrix.android.internal.di.SessionCacheDirectory
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
|
import im.vector.matrix.android.internal.di.SessionFilesDirectory
|
||||||
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionModule
|
import im.vector.matrix.android.internal.session.SessionModule
|
||||||
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue