Skip to content
Snippets Groups Projects
Commit d51291f8 authored by Benoit Marty's avatar Benoit Marty
Browse files

Merge branch 'release/1.4.27' into main

parents 6b2103d1 e8bcc74c
No related branches found
No related tags found
No related merge requests found
Showing
with 300 additions and 87 deletions
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">
......
Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/main/CHANGES.md Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/main/CHANGES.md
Changes in Matrix-SDK 1.4.27 (2022-07-20)
===================================================
Imported from Element 1.4.27-RC2. (https://github.com/vector-im/element-android/releases/tag/v1.4.27-RC2)
SDK API changes ⚠️
------------------
- Group all location sharing related API into LocationSharingService ([#5864](https://github.com/vector-im/element-android/issues/5864))
- Add support for MSC2457 - opting in or out of logging out all devices when changing password ([#6191](https://github.com/vector-im/element-android/issues/6191))
- Create `QueryStateEventValue` to do query on `stateKey` for State Event. Also remove the default parameter values for those type. ([#6319](https://github.com/vector-im/element-android/issues/6319))
Changes in Matrix-SDK 1.4.25 (2022-06-29) Changes in Matrix-SDK 1.4.25 (2022-06-29)
=================================================== ===================================================
......
...@@ -15,7 +15,7 @@ buildscript { ...@@ -15,7 +15,7 @@ buildscript {
classpath libs.gradle.gradlePlugin classpath libs.gradle.gradlePlugin
classpath libs.gradle.kotlinPlugin classpath libs.gradle.kotlinPlugin
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0' classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21" classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.0"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
......
...@@ -13,7 +13,7 @@ ext.versions = [ ...@@ -13,7 +13,7 @@ ext.versions = [
def gradle = "7.1.3" def gradle = "7.1.3"
// Ref: https://kotlinlang.org/releases.html // Ref: https://kotlinlang.org/releases.html
def kotlin = "1.6.21" def kotlin = "1.6.21"
def kotlinCoroutines = "1.6.2" def kotlinCoroutines = "1.6.3"
def dagger = "2.42" def dagger = "2.42"
def retrofit = "2.9.0" def retrofit = "2.9.0"
def arrow = "0.8.2" def arrow = "0.8.2"
...@@ -21,20 +21,21 @@ def markwon = "4.6.2" ...@@ -21,20 +21,21 @@ def markwon = "4.6.2"
def moshi = "1.13.0" def moshi = "1.13.0"
def lifecycle = "2.4.1" def lifecycle = "2.4.1"
def flowBinding = "1.2.0" def flowBinding = "1.2.0"
def flipper = "0.151.1"
def epoxy = "4.6.2" def epoxy = "4.6.2"
def mavericks = "2.6.1" def mavericks = "2.7.0"
def glide = "4.13.2" def glide = "4.13.2"
def bigImageViewer = "1.8.1" def bigImageViewer = "1.8.1"
def jjwt = "0.11.5" def jjwt = "0.11.5"
def vanniktechEmoji = "0.15.0" def vanniktechEmoji = "0.15.0"
def fragment = "1.4.1"
// Testing // Testing
def mockk = "1.12.4" def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
def espresso = "3.4.0" def espresso = "3.4.0"
def androidxTest = "1.4.0" def androidxTest = "1.4.0"
def androidxOrchestrator = "1.4.1" def androidxOrchestrator = "1.4.1"
ext.libs = [ ext.libs = [
gradle : [ gradle : [
'gradlePlugin' : "com.android.tools.build:gradle:$gradle", 'gradlePlugin' : "com.android.tools.build:gradle:$gradle",
...@@ -48,12 +49,16 @@ ext.libs = [ ...@@ -48,12 +49,16 @@ ext.libs = [
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines" 'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
], ],
androidx : [ androidx : [
'annotation' : "androidx.annotation:annotation:1.4.0",
'activity' : "androidx.activity:activity:1.4.0", 'activity' : "androidx.activity:activity:1.4.0",
'annotations' : "androidx.annotation:annotation:1.3.0",
'appCompat' : "androidx.appcompat:appcompat:1.4.2", 'appCompat' : "androidx.appcompat:appcompat:1.4.2",
'biometric' : "androidx.biometric:biometric:1.1.0",
'core' : "androidx.core:core-ktx:1.8.0", 'core' : "androidx.core:core-ktx:1.8.0",
'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1", 'recyclerview' : "androidx.recyclerview:recyclerview:1.2.1",
'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3", 'exifinterface' : "androidx.exifinterface:exifinterface:1.3.3",
'fragmentKtx' : "androidx.fragment:fragment-ktx:1.4.1", 'fragmentKtx' : "androidx.fragment:fragment-ktx:$fragment",
'fragmentTesting' : "androidx.fragment:fragment-testing:$fragment",
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.4", 'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.4",
'work' : "androidx.work:work-runtime-ktx:2.7.1", 'work' : "androidx.work:work-runtime-ktx:2.7.1",
'autoFill' : "androidx.autofill:autofill:1.1.0", 'autoFill' : "androidx.autofill:autofill:1.1.0",
...@@ -84,10 +89,16 @@ ext.libs = [ ...@@ -84,10 +89,16 @@ ext.libs = [
'dagger' : "com.google.dagger:dagger:$dagger", 'dagger' : "com.google.dagger:dagger:$dagger",
'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger", 'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger",
'hilt' : "com.google.dagger:hilt-android:$dagger", 'hilt' : "com.google.dagger:hilt-android:$dagger",
'hiltAndroidTesting' : "com.google.dagger:hilt-android-testing:$dagger",
'hiltCompiler' : "com.google.dagger:hilt-compiler:$dagger" 'hiltCompiler' : "com.google.dagger:hilt-compiler:$dagger"
], ],
flipper : [
'flipper' : "com.facebook.flipper:flipper:$flipper",
'flipperNetworkPlugin' : "com.facebook.flipper:flipper-network-plugin:$flipper",
],
squareup : [ squareup : [
'moshi' : "com.squareup.moshi:moshi:$moshi", 'moshi' : "com.squareup.moshi:moshi:$moshi",
'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi",
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi", 'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit", 'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit" 'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
...@@ -153,3 +164,5 @@ ext.libs = [ ...@@ -153,3 +164,5 @@ ext.libs = [
'junit' : "junit:junit:4.13.2" 'junit' : "junit:junit:4.13.2"
] ]
] ]
...@@ -9,6 +9,7 @@ ext.groups = [ ...@@ -9,6 +9,7 @@ ext.groups = [
'com.github.jetradarmobile', 'com.github.jetradarmobile',
'com.github.MatrixFrog', 'com.github.MatrixFrog',
'com.github.tapadoo', 'com.github.tapadoo',
'com.github.UnifiedPush',
'com.github.vector-im', 'com.github.vector-im',
'com.github.yalantis', 'com.github.yalantis',
'com.github.Zhuinden', 'com.github.Zhuinden',
...@@ -31,6 +32,7 @@ ext.groups = [ ...@@ -31,6 +32,7 @@ ext.groups = [
], ],
group: [ group: [
'com.android', 'com.android',
'com.android.ndk.thirdparty',
'com.android.tools', 'com.android.tools',
'com.google.firebase', 'com.google.firebase',
'com.google.testing.platform', 'com.google.testing.platform',
...@@ -52,6 +54,7 @@ ext.groups = [ ...@@ -52,6 +54,7 @@ ext.groups = [
'com.dropbox.core', 'com.dropbox.core',
'com.soywiz.korlibs.korte', 'com.soywiz.korlibs.korte',
'com.facebook.fbjni', 'com.facebook.fbjni',
'com.facebook.flipper',
'com.facebook.fresco', 'com.facebook.fresco',
'com.facebook.infer.annotation', 'com.facebook.infer.annotation',
'com.facebook.soloader', 'com.facebook.soloader',
...@@ -93,6 +96,7 @@ ext.groups = [ ...@@ -93,6 +96,7 @@ ext.groups = [
'com.ibm.icu', 'com.ibm.icu',
'com.jakewharton.android.repackaged', 'com.jakewharton.android.repackaged',
'com.jakewharton.timber', 'com.jakewharton.timber',
'com.kgurgul.flipper',
'com.linkedin.dexmaker', 'com.linkedin.dexmaker',
'com.mapbox.mapboxsdk', 'com.mapbox.mapboxsdk',
'com.nulab-inc', 'com.nulab-inc',
...@@ -168,6 +172,7 @@ ext.groups = [ ...@@ -168,6 +172,7 @@ ext.groups = [
'org.glassfish.jaxb', 'org.glassfish.jaxb',
'org.hamcrest', 'org.hamcrest',
'org.jacoco', 'org.jacoco',
'org.java-websocket',
'org.jetbrains', 'org.jetbrains',
'org.jetbrains.dokka', 'org.jetbrains.dokka',
'org.jetbrains.intellij.deps', 'org.jetbrains.intellij.deps',
......
...@@ -26,7 +26,7 @@ vector.httpLogLevel=NONE ...@@ -26,7 +26,7 @@ vector.httpLogLevel=NONE
# Ref: https://github.com/vanniktech/gradle-maven-publish-plugin # Ref: https://github.com/vanniktech/gradle-maven-publish-plugin
GROUP=org.matrix.android GROUP=org.matrix.android
POM_ARTIFACT_ID=matrix-android-sdk2 POM_ARTIFACT_ID=matrix-android-sdk2
VERSION_NAME=1.4.25 VERSION_NAME=1.4.27
POM_PACKAGING=aar POM_PACKAGING=aar
......
...@@ -7,6 +7,10 @@ apply plugin: "org.jetbrains.dokka" ...@@ -7,6 +7,10 @@ apply plugin: "org.jetbrains.dokka"
// WARNING: always restore this line after importing code from Element Android (1/2) // WARNING: always restore this line after importing code from Element Android (1/2)
apply plugin: "com.vanniktech.maven.publish" apply plugin: "com.vanniktech.maven.publish"
if (project.hasProperty("coverage")) {
apply plugin: 'jacoco'
}
buildscript { buildscript {
repositories { repositories {
// Do not use `mavenCentral()`, it prevents Dependabot from working properly // Do not use `mavenCentral()`, it prevents Dependabot from working properly
...@@ -77,7 +81,9 @@ android { ...@@ -77,7 +81,9 @@ android {
buildTypes { buildTypes {
debug { debug {
testCoverageEnabled true if (project.hasProperty("coverage")) {
testCoverageEnabled = coverage.enableTestCoverage
}
// Set to true to log privacy or sensible data, such as token // Set to true to log privacy or sensible data, such as token
buildConfigField "boolean", "LOG_PRIVATE_DATA", project.property("vector.debugPrivateData") buildConfigField "boolean", "LOG_PRIVATE_DATA", project.property("vector.debugPrivateData")
// Set to BODY instead of NONE to enable logging // Set to BODY instead of NONE to enable logging
...@@ -196,7 +202,7 @@ dependencies { ...@@ -196,7 +202,7 @@ dependencies {
implementation libs.apache.commonsImaging implementation libs.apache.commonsImaging
// Phone number https://github.com/google/libphonenumber // Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.50' implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.51'
testImplementation libs.tests.junit testImplementation libs.tests.junit
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
......
/* /*
* Copyright (c) 2021 The Matrix.org Foundation C.I.C. * Copyright 2022 The Matrix.org Foundation C.I.C.
* *
* 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,9 +14,9 @@ ...@@ -14,9 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.internal.session.securestorage package org.matrix.android.sdk
import org.matrix.android.sdk.internal.util.system.BuildVersionSdkIntProvider import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
class TestBuildVersionSdkIntProvider : BuildVersionSdkIntProvider { class TestBuildVersionSdkIntProvider : BuildVersionSdkIntProvider {
var value: Int = 0 var value: Int = 0
......
...@@ -14,40 +14,57 @@ ...@@ -14,40 +14,57 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.internal.session.securestorage package org.matrix.android.sdk.api.securestorage
import android.os.Build import android.os.Build
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.spyk
import org.amshove.kluent.invoking
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeInstanceOf
import org.amshove.kluent.shouldNotThrow
import org.amshove.kluent.shouldThrow
import org.junit.Before
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
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.TestBuildVersionSdkIntProvider
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.api.util.toBase64NoPadding
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.security.KeyStore
import java.security.KeyStoreException
import java.util.UUID import java.util.UUID
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class SecretStoringUtilsTest : InstrumentedTest { class SecretStoringUtilsTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val buildVersionSdkIntProvider = TestBuildVersionSdkIntProvider() private val buildVersionSdkIntProvider = TestBuildVersionSdkIntProvider()
private val secretStoringUtils = SecretStoringUtils(context(), buildVersionSdkIntProvider) private val keyStore = spyk(KeyStore.getInstance("AndroidKeyStore")).also { it.load(null) }
private val secretStoringUtils = SecretStoringUtils(context, keyStore, buildVersionSdkIntProvider)
companion object { companion object {
const val TEST_STR = "This is something I want to store safely!" const val TEST_STR = "This is something I want to store safely!"
} }
@Before
fun setup() {
clearAllMocks()
}
@Test @Test
fun testStringNominalCaseApi21() { fun testStringNominalCaseApi21() {
val alias = generateAlias() val alias = generateAlias()
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
// Encrypt // Encrypt
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias) val encrypted = secretStoringUtils.securelyStoreBytes(TEST_STR.toByteArray(), alias)
// Decrypt // Decrypt
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias) val decrypted = String(secretStoringUtils.loadSecureSecretBytes(encrypted, alias))
decrypted shouldBeEqualTo TEST_STR decrypted shouldBeEqualTo TEST_STR
secretStoringUtils.safeDeleteKey(alias) secretStoringUtils.safeDeleteKey(alias)
} }
...@@ -57,9 +74,9 @@ class SecretStoringUtilsTest : InstrumentedTest { ...@@ -57,9 +74,9 @@ class SecretStoringUtilsTest : InstrumentedTest {
val alias = generateAlias() val alias = generateAlias()
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
// Encrypt // Encrypt
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias) val encrypted = secretStoringUtils.securelyStoreBytes(TEST_STR.toByteArray(), alias)
// Decrypt // Decrypt
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias) val decrypted = String(secretStoringUtils.loadSecureSecretBytes(encrypted, alias))
decrypted shouldBeEqualTo TEST_STR decrypted shouldBeEqualTo TEST_STR
secretStoringUtils.safeDeleteKey(alias) secretStoringUtils.safeDeleteKey(alias)
} }
...@@ -69,9 +86,9 @@ class SecretStoringUtilsTest : InstrumentedTest { ...@@ -69,9 +86,9 @@ class SecretStoringUtilsTest : InstrumentedTest {
val alias = generateAlias() val alias = generateAlias()
buildVersionSdkIntProvider.value = Build.VERSION_CODES.R buildVersionSdkIntProvider.value = Build.VERSION_CODES.R
// Encrypt // Encrypt
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias) val encrypted = secretStoringUtils.securelyStoreBytes(TEST_STR.toByteArray(), alias)
// Decrypt // Decrypt
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias) val decrypted = String(secretStoringUtils.loadSecureSecretBytes(encrypted, alias))
decrypted shouldBeEqualTo TEST_STR decrypted shouldBeEqualTo TEST_STR
secretStoringUtils.safeDeleteKey(alias) secretStoringUtils.safeDeleteKey(alias)
} }
...@@ -81,13 +98,13 @@ class SecretStoringUtilsTest : InstrumentedTest { ...@@ -81,13 +98,13 @@ class SecretStoringUtilsTest : InstrumentedTest {
val alias = generateAlias() val alias = generateAlias()
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
// Encrypt // Encrypt
val encrypted = secretStoringUtils.securelyStoreString(TEST_STR, alias) val encrypted = secretStoringUtils.securelyStoreBytes(TEST_STR.toByteArray(), alias)
// Simulate a system upgrade // Simulate a system upgrade
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
// Decrypt // Decrypt
val decrypted = secretStoringUtils.loadSecureSecret(encrypted, alias) val decrypted = String(secretStoringUtils.loadSecureSecretBytes(encrypted, alias))
decrypted shouldBeEqualTo TEST_STR decrypted shouldBeEqualTo TEST_STR
secretStoringUtils.safeDeleteKey(alias) secretStoringUtils.safeDeleteKey(alias)
} }
...@@ -180,5 +197,56 @@ class SecretStoringUtilsTest : InstrumentedTest { ...@@ -180,5 +197,56 @@ class SecretStoringUtilsTest : InstrumentedTest {
secretStoringUtils.safeDeleteKey(alias) secretStoringUtils.safeDeleteKey(alias)
} }
@Test
fun testEnsureKeyReturnsSymmetricKeyOnAndroidM() {
buildVersionSdkIntProvider.value = Build.VERSION_CODES.M
val alias = generateAlias()
val key = secretStoringUtils.ensureKey(alias)
key shouldBeInstanceOf KeyStore.SecretKeyEntry::class
secretStoringUtils.safeDeleteKey(alias)
}
@Test
fun testEnsureKeyReturnsPrivateKeyOnAndroidL() {
buildVersionSdkIntProvider.value = Build.VERSION_CODES.LOLLIPOP
val alias = generateAlias()
val key = secretStoringUtils.ensureKey(alias)
key shouldBeInstanceOf KeyStore.PrivateKeyEntry::class
secretStoringUtils.safeDeleteKey(alias)
}
@Test
fun testSafeDeleteCanHandleKeyStoreExceptions() {
every { keyStore.deleteEntry(any()) } throws KeyStoreException()
invoking { secretStoringUtils.safeDeleteKey(generateAlias()) } shouldNotThrow KeyStoreException::class
}
@Test
fun testLoadSecureSecretBytesWillThrowOnInvalidStreamFormat() {
invoking {
secretStoringUtils.loadSecureSecretBytes(byteArrayOf(255.toByte()), generateAlias())
} shouldThrow IllegalArgumentException::class
}
@Test
fun testLoadSecureSecretWillThrowOnInvalidStreamFormat() {
invoking {
secretStoringUtils.loadSecureSecret(byteArrayOf(255.toByte()).inputStream(), generateAlias())
} shouldThrow IllegalArgumentException::class
}
private fun generateAlias() = UUID.randomUUID().toString() private fun generateAlias() = UUID.randomUUID().toString()
} }
private fun ByteArray.toBase64NoPadding(): String {
return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP)
}
private fun String.fromBase64(): ByteArray {
return Base64.decode(this, Base64.DEFAULT)
}
...@@ -20,7 +20,9 @@ import android.content.Context ...@@ -20,7 +20,9 @@ import android.content.Context
import dagger.BindsInstance import dagger.BindsInstance
import dagger.Component import dagger.Component
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.securestorage.SecureStorageModule
import org.matrix.android.sdk.internal.auth.AuthModule import org.matrix.android.sdk.internal.auth.AuthModule
import org.matrix.android.sdk.internal.debug.DebugModule
import org.matrix.android.sdk.internal.di.MatrixComponent import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.di.MatrixModule import org.matrix.android.sdk.internal.di.MatrixModule
import org.matrix.android.sdk.internal.di.MatrixScope import org.matrix.android.sdk.internal.di.MatrixScope
...@@ -36,8 +38,10 @@ import org.matrix.android.sdk.internal.util.system.SystemModule ...@@ -36,8 +38,10 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
NetworkModule::class, NetworkModule::class,
AuthModule::class, AuthModule::class,
RawModule::class, RawModule::class,
DebugModule::class,
SettingsModule::class, SettingsModule::class,
SystemModule::class SystemModule::class,
SecureStorageModule::class,
] ]
) )
@MatrixScope @MatrixScope
...@@ -49,7 +53,7 @@ internal interface TestMatrixComponent : MatrixComponent { ...@@ -49,7 +53,7 @@ internal interface TestMatrixComponent : MatrixComponent {
interface Factory { interface Factory {
fun create( fun create(
@BindsInstance context: Context, @BindsInstance context: Context,
@BindsInstance matrixConfiguration: MatrixConfiguration @BindsInstance matrixConfiguration: MatrixConfiguration,
): TestMatrixComponent ): TestMatrixComponent
} }
} }
...@@ -28,6 +28,7 @@ import org.junit.runner.RunWith ...@@ -28,6 +28,7 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4 import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.getStateEvent
...@@ -73,27 +74,37 @@ class SpaceCreationTest : InstrumentedTest { ...@@ -73,27 +74,37 @@ class SpaceCreationTest : InstrumentedTest {
// assertEquals(topic, syncedSpace.asRoom().roomSummary()?., "Room topic should be set") // assertEquals(topic, syncedSpace.asRoom().roomSummary()?., "Room topic should be set")
assertNotNull("Space should be found by Id", syncedSpace) assertNotNull("Space should be found by Id", syncedSpace)
val creationEvent = syncedSpace!!.asRoom().getStateEvent(EventType.STATE_ROOM_CREATE) val createContent = syncedSpace!!.asRoom()
val createContent = creationEvent?.content.toModel<RoomCreateContent>() .getStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)
?.content
?.toModel<RoomCreateContent>()
assertEquals("Room type should be space", RoomType.SPACE, createContent?.type) assertEquals("Room type should be space", RoomType.SPACE, createContent?.type)
var powerLevelsContent: PowerLevelsContent? = null var powerLevelsContent: PowerLevelsContent? = null
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
val toModel = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)?.content.toModel<PowerLevelsContent>() powerLevelsContent = syncedSpace.asRoom()
powerLevelsContent = toModel .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
toModel != null ?.content
?.toModel<PowerLevelsContent>()
powerLevelsContent != null
} }
} }
assertEquals("Space-rooms should be created with a power level for events_default of 100", 100, powerLevelsContent?.eventsDefault) assertEquals("Space-rooms should be created with a power level for events_default of 100", 100, powerLevelsContent?.eventsDefault)
val guestAccess = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_GUEST_ACCESS)?.content val guestAccess = syncedSpace.asRoom()
?.toModel<RoomGuestAccessContent>()?.guestAccess .getStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.IsEmpty)
?.content
?.toModel<RoomGuestAccessContent>()
?.guestAccess
assertEquals("Public space room should be peekable by guest", GuestAccess.CanJoin, guestAccess) assertEquals("Public space room should be peekable by guest", GuestAccess.CanJoin, guestAccess)
val historyVisibility = syncedSpace.asRoom().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY)?.content val historyVisibility = syncedSpace.asRoom()
?.toModel<RoomHistoryVisibilityContent>()?.historyVisibility .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)
?.content
?.toModel<RoomHistoryVisibilityContent>()
?.historyVisibility
assertEquals("Public space room should be world readable", RoomHistoryVisibility.WORLD_READABLE, historyVisibility) assertEquals("Public space room should be world readable", RoomHistoryVisibility.WORLD_READABLE, historyVisibility)
} }
......
...@@ -569,8 +569,9 @@ class SpaceHierarchyTest : InstrumentedTest { ...@@ -569,8 +569,9 @@ class SpaceHierarchyTest : InstrumentedTest {
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
val room = bobSession.getRoom(bobRoomId)!! val room = bobSession.getRoom(bobRoomId)!!
val currentPLContent = room val currentPLContent = room
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
?.let { it.content.toModel<PowerLevelsContent>() } ?.content
.toModel<PowerLevelsContent>()
val newPowerLevelsContent = currentPLContent val newPowerLevelsContent = currentPLContent
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
...@@ -583,7 +584,7 @@ class SpaceHierarchyTest : InstrumentedTest { ...@@ -583,7 +584,7 @@ class SpaceHierarchyTest : InstrumentedTest {
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
commonTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!! val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!!
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
?.content ?.content
?.toModel<PowerLevelsContent>() ?.toModel<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) } ?.let { PowerLevelsHelper(it) }
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
package org.matrix.android.sdk.api package org.matrix.android.sdk.api
import android.content.Context import android.content.Context
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
...@@ -25,10 +27,12 @@ import com.zhuinden.monarchy.Monarchy ...@@ -25,10 +27,12 @@ import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.debug.DebugService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.network.ApiInterceptorListener import org.matrix.android.sdk.api.network.ApiInterceptorListener
import org.matrix.android.sdk.api.network.ApiPath import org.matrix.android.sdk.api.network.ApiPath
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.securestorage.SecureStorageService
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.di.DaggerMatrixComponent import org.matrix.android.sdk.internal.di.DaggerMatrixComponent
...@@ -54,6 +58,7 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) { ...@@ -54,6 +58,7 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter @Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService @Inject internal lateinit var authenticationService: AuthenticationService
@Inject internal lateinit var rawService: RawService @Inject internal lateinit var rawService: RawService
@Inject internal lateinit var debugService: DebugService
@Inject internal lateinit var userAgentHolder: UserAgentHolder @Inject internal lateinit var userAgentHolder: UserAgentHolder
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver @Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
@Inject internal lateinit var olmManager: OlmManager @Inject internal lateinit var olmManager: OlmManager
...@@ -62,6 +67,9 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) { ...@@ -62,6 +67,9 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var apiInterceptor: ApiInterceptor @Inject internal lateinit var apiInterceptor: ApiInterceptor
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
@Inject internal lateinit var lightweightSettingsStorage: LightweightSettingsStorage @Inject internal lateinit var lightweightSettingsStorage: LightweightSettingsStorage
@Inject internal lateinit var secureStorageService: SecureStorageService
private val uiHandler = Handler(Looper.getMainLooper())
init { init {
val appContext = context.applicationContext val appContext = context.applicationContext
...@@ -74,7 +82,9 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) { ...@@ -74,7 +82,9 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
.build() .build()
WorkManager.initialize(appContext, configuration) WorkManager.initialize(appContext, configuration)
} }
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
} }
/** /**
...@@ -93,6 +103,11 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) { ...@@ -93,6 +103,11 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
*/ */
fun rawService() = rawService fun rawService() = rawService
/**
* Return the DebugService.
*/
fun debugService() = debugService
/** /**
* Return the LightweightSettingsStorage. * Return the LightweightSettingsStorage.
*/ */
...@@ -108,6 +123,11 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) { ...@@ -108,6 +123,11 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
*/ */
fun legacySessionImporter() = legacySessionImporter fun legacySessionImporter() = legacySessionImporter
/**
* Returns the SecureStorageService used to encrypt and decrypt sensitive data.
*/
fun secureStorageService(): SecureStorageService = secureStorageService
/** /**
* Get the worker factory. The returned value has to be provided to `WorkConfiguration.Builder()`. * Get the worker factory. The returned value has to be provided to `WorkConfiguration.Builder()`.
*/ */
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.matrix.android.sdk.api package org.matrix.android.sdk.api
import okhttp3.ConnectionSpec import okhttp3.ConnectionSpec
import okhttp3.Interceptor
import org.matrix.android.sdk.api.crypto.MXCryptoConfig import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import java.net.Proxy import java.net.Proxy
...@@ -65,4 +66,8 @@ data class MatrixConfiguration( ...@@ -65,4 +66,8 @@ data class MatrixConfiguration(
* Thread messages default enable/disabled value. * Thread messages default enable/disabled value.
*/ */
val threadMessagesEnabledDefault: Boolean = false, val threadMessagesEnabledDefault: Boolean = false,
/**
* List of network interceptors, they will be added when building an OkHttp client.
*/
val networkInterceptors: List<Interceptor> = emptyList(),
) )
...@@ -21,5 +21,6 @@ data class LoginFlowResult( ...@@ -21,5 +21,6 @@ data class LoginFlowResult(
val ssoIdentityProviders: List<SsoIdentityProvider>?, val ssoIdentityProviders: List<SsoIdentityProvider>?,
val isLoginAndRegistrationSupported: Boolean, val isLoginAndRegistrationSupported: Boolean,
val homeServerUrl: String, val homeServerUrl: String,
val isOutdatedHomeserver: Boolean val isOutdatedHomeserver: Boolean,
val isLogoutDevicesSupported: Boolean
) )
...@@ -72,7 +72,9 @@ interface LoginWizard { ...@@ -72,7 +72,9 @@ interface LoginWizard {
* Confirm the new password, once the user has checked their email * Confirm the new password, once the user has checked their email
* When this method succeed, tha account password will be effectively modified. * When this method succeed, tha account password will be effectively modified.
* *
* @param newPassword the desired new password * @param newPassword the desired new password.
* @param logoutAllDevices defaults to true, all devices will be logged out. False values will only be taken into account
* if [org.matrix.android.sdk.api.auth.data.LoginFlowResult.isLogoutDevicesSupported] is true.
*/ */
suspend fun resetPasswordMailConfirmed(newPassword: String) suspend fun resetPasswordMailConfirmed(newPassword: String, logoutAllDevices: Boolean = true)
} }
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.debug
import io.realm.RealmConfiguration
/**
* Useful methods to access to some private data managed by the SDK.
*/
interface DebugService {
/**
* Get all the available Realm Configuration.
*/
fun getAllRealmConfigurations(): List<RealmConfiguration>
/**
* Prints out info on DB size to logcat.
*/
fun logDbUsageInfo()
}
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
package org.matrix.android.sdk.api.query package org.matrix.android.sdk.api.query
/**
* Only a subset of [QueryStringValue] are applicable to query the `stateKey` of a state event.
*/
sealed interface QueryStateEventValue
/** /**
* Basic query language. All these cases are mutually exclusive. * Basic query language. All these cases are mutually exclusive.
*/ */
...@@ -33,22 +38,22 @@ sealed interface QueryStringValue { ...@@ -33,22 +38,22 @@ sealed interface QueryStringValue {
/** /**
* The tested field has to be not null. * The tested field has to be not null.
*/ */
object IsNotNull : QueryStringValue object IsNotNull : QueryStringValue, QueryStateEventValue
/** /**
* The tested field has to be empty. * The tested field has to be empty.
*/ */
object IsEmpty : QueryStringValue object IsEmpty : QueryStringValue, QueryStateEventValue
/** /**
* The tested field has to not empty. * The tested field has to be not empty.
*/ */
object IsNotEmpty : QueryStringValue object IsNotEmpty : QueryStringValue, QueryStateEventValue
/** /**
* Interface to check String content. * Interface to check String content.
*/ */
sealed interface ContentQueryStringValue : QueryStringValue { sealed interface ContentQueryStringValue : QueryStringValue, QueryStateEventValue {
val string: String val string: String
val case: Case val case: Case
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
@file:Suppress("DEPRECATION") @file:Suppress("DEPRECATION")
package org.matrix.android.sdk.internal.session.securestorage package org.matrix.android.sdk.api.securestorage
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
...@@ -25,7 +25,7 @@ import android.security.KeyPairGeneratorSpec ...@@ -25,7 +25,7 @@ import android.security.KeyPairGeneratorSpec
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties import android.security.keystore.KeyProperties
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import org.matrix.android.sdk.internal.util.system.BuildVersionSdkIntProvider import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
import timber.log.Timber import timber.log.Timber
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
...@@ -80,9 +80,11 @@ import javax.security.auth.x500.X500Principal ...@@ -80,9 +80,11 @@ import javax.security.auth.x500.X500Principal
* Important: Keys stored in the keystore can be wiped out (depends of the OS version, like for example if you * Important: Keys stored in the keystore can be wiped out (depends of the OS version, like for example if you
* add a pin or change the schema); So you might and with a useless pile of bytes. * add a pin or change the schema); So you might and with a useless pile of bytes.
*/ */
internal class SecretStoringUtils @Inject constructor( class SecretStoringUtils @Inject constructor(
private val context: Context, private val context: Context,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider private val keyStore: KeyStore,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
private val keyNeedsUserAuthentication: Boolean = false,
) { ) {
companion object { companion object {
...@@ -94,14 +96,24 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -94,14 +96,24 @@ internal class SecretStoringUtils @Inject constructor(
private const val FORMAT_1: Byte = 1 private const val FORMAT_1: Byte = 1
} }
private val keyStore: KeyStore by lazy { private val secureRandom = SecureRandom()
KeyStore.getInstance(ANDROID_KEY_STORE).apply {
load(null) /**
* Allows creation of the crypto keys associated witht he [alias] before encrypting some value with it.
* @return A [KeyStore.Entry] with the keys.
*/
@SuppressLint("NewApi")
fun ensureKey(alias: String): KeyStore.Entry {
when {
buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> getOrGenerateSymmetricKeyForAliasM(alias)
else -> getOrGenerateKeyPairForAlias(alias).privateKey
} }
return keyStore.getEntry(alias, null)
} }
private val secureRandom = SecureRandom() /**
* Deletes the key associated with the [keyAlias] and logs any [KeyStoreException] that could happen.
*/
fun safeDeleteKey(keyAlias: String) { fun safeDeleteKey(keyAlias: String) {
try { try {
keyStore.deleteEntry(keyAlias) keyStore.deleteEntry(keyAlias)
...@@ -121,24 +133,24 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -121,24 +133,24 @@ internal class SecretStoringUtils @Inject constructor(
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Throws(Exception::class) @Throws(Exception::class)
fun securelyStoreString(secret: String, keyAlias: String): ByteArray { fun securelyStoreBytes(secret: ByteArray, keyAlias: String): ByteArray {
return when { return when {
buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias) buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> encryptBytesM(secret, keyAlias)
else -> encryptString(secret, keyAlias) else -> encryptBytes(secret, keyAlias)
} }
} }
/** /**
* Decrypt a secret that was encrypted by #securelyStoreString(). * Decrypt a secret that was encrypted by [securelyStoreBytes].
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Throws(Exception::class) @Throws(Exception::class)
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String { fun loadSecureSecretBytes(encrypted: ByteArray, keyAlias: String): ByteArray {
encrypted.inputStream().use { inputStream -> encrypted.inputStream().use { inputStream ->
// First get the format // First get the format
return when (val format = inputStream.read().toByte()) { return when (val format = inputStream.read().toByte()) {
FORMAT_API_M -> decryptStringM(inputStream, keyAlias) FORMAT_API_M -> decryptBytesM(inputStream, keyAlias)
FORMAT_1 -> decryptString(inputStream, keyAlias) FORMAT_1 -> decryptBytes(inputStream, keyAlias)
else -> throw IllegalArgumentException("Unknown format $format") else -> throw IllegalArgumentException("Unknown format $format")
} }
} }
...@@ -162,6 +174,22 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -162,6 +174,22 @@ internal class SecretStoringUtils @Inject constructor(
} }
} }
fun getEncryptCipher(alias: String): Cipher {
val key = when (val keyEntry = ensureKey(alias)) {
is KeyStore.SecretKeyEntry -> keyEntry.secretKey
is KeyStore.PrivateKeyEntry -> keyEntry.certificate.publicKey
else -> throw IllegalStateException("Unknown KeyEntry type.")
}
val cipherMode = when {
buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.M -> AES_MODE
else -> RSA_MODE
}
val cipher = Cipher.getInstance(cipherMode)
cipher.init(Cipher.ENCRYPT_MODE, key)
return cipher
}
@SuppressLint("NewApi")
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
private fun getOrGenerateSymmetricKeyForAliasM(alias: String): SecretKey { private fun getOrGenerateSymmetricKeyForAliasM(alias: String): SecretKey {
val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry)
...@@ -176,6 +204,13 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -176,6 +204,13 @@ internal class SecretStoringUtils @Inject constructor(
.setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(128) .setKeySize(128)
.apply {
setUserAuthenticationRequired(keyNeedsUserAuthentication)
if (buildVersionSdkIntProvider.get() >= Build.VERSION_CODES.N) {
setInvalidatedByBiometricEnrollment(true)
}
}
.setUserAuthenticationRequired(keyNeedsUserAuthentication)
.build() .build()
generator.init(keyGenSpec) generator.init(keyGenSpec)
return generator.generateKey() return generator.generateKey()
...@@ -216,19 +251,16 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -216,19 +251,16 @@ internal class SecretStoringUtils @Inject constructor(
} }
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
private fun encryptStringM(text: String, keyAlias: String): ByteArray { private fun encryptBytesM(byteArray: ByteArray, keyAlias: String): ByteArray {
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias) val cipher = getEncryptCipher(keyAlias)
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.iv val iv = cipher.iv
// we happen the iv to the final result // we happen the iv to the final result
val encryptedBytes: ByteArray = cipher.doFinal(text.toByteArray(Charsets.UTF_8)) val encryptedBytes: ByteArray = cipher.doFinal(byteArray)
return formatMMake(iv, encryptedBytes) return formatMMake(iv, encryptedBytes)
} }
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
private fun decryptStringM(inputStream: InputStream, keyAlias: String): String { private fun decryptBytesM(inputStream: InputStream, keyAlias: String): ByteArray {
val (iv, encryptedText) = formatMExtract(inputStream) val (iv, encryptedText) = formatMExtract(inputStream)
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias) val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias)
...@@ -237,10 +269,10 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -237,10 +269,10 @@ internal class SecretStoringUtils @Inject constructor(
val spec = GCMParameterSpec(128, iv) val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec) cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
return String(cipher.doFinal(encryptedText), Charsets.UTF_8) return cipher.doFinal(encryptedText)
} }
private fun encryptString(text: String, keyAlias: String): ByteArray { private fun encryptBytes(byteArray: ByteArray, keyAlias: String): ByteArray {
// we generate a random symmetric key // we generate a random symmetric key
val key = ByteArray(16) val key = ByteArray(16)
secureRandom.nextBytes(key) secureRandom.nextBytes(key)
...@@ -252,12 +284,12 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -252,12 +284,12 @@ internal class SecretStoringUtils @Inject constructor(
val cipher = Cipher.getInstance(AES_MODE) val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, sKey) cipher.init(Cipher.ENCRYPT_MODE, sKey)
val iv = cipher.iv val iv = cipher.iv
val encryptedBytes: ByteArray = cipher.doFinal(text.toByteArray(Charsets.UTF_8)) val encryptedBytes: ByteArray = cipher.doFinal(byteArray)
return format1Make(encryptedKey, iv, encryptedBytes) return format1Make(encryptedKey, iv, encryptedBytes)
} }
private fun decryptString(inputStream: InputStream, keyAlias: String): String { private fun decryptBytes(inputStream: InputStream, keyAlias: String): ByteArray {
val (encryptedKey, iv, encrypted) = format1Extract(inputStream) val (encryptedKey, iv, encrypted) = format1Extract(inputStream)
// we need to decrypt the key // we need to decrypt the key
...@@ -266,16 +298,13 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -266,16 +298,13 @@ internal class SecretStoringUtils @Inject constructor(
val spec = GCMParameterSpec(128, iv) val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec) cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
return String(cipher.doFinal(encrypted), Charsets.UTF_8) return cipher.doFinal(encrypted)
} }
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
@Throws(IOException::class) @Throws(IOException::class)
private fun saveSecureObjectM(keyAlias: String, output: OutputStream, writeObject: Any) { private fun saveSecureObjectM(keyAlias: String, output: OutputStream, writeObject: Any) {
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias) val cipher = getEncryptCipher(keyAlias)
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, secretKey/*, spec*/)
val iv = cipher.iv val iv = cipher.iv
val bos1 = ByteArrayOutputStream() val bos1 = ByteArrayOutputStream()
...@@ -362,10 +391,8 @@ internal class SecretStoringUtils @Inject constructor( ...@@ -362,10 +391,8 @@ internal class SecretStoringUtils @Inject constructor(
@Throws(Exception::class) @Throws(Exception::class)
private fun rsaEncrypt(alias: String, secret: ByteArray): ByteArray { private fun rsaEncrypt(alias: String, secret: ByteArray): ByteArray {
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias)
// Encrypt the text // Encrypt the text
val inputCipher = Cipher.getInstance(RSA_MODE) val inputCipher = getEncryptCipher(alias)
inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.certificate.publicKey)
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()
CipherOutputStream(outputStream, inputCipher).use { CipherOutputStream(outputStream, inputCipher).use {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment