diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
index 21b4ffa05c..50f55577d0 100644
--- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
+++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
@@ -31,7 +31,7 @@ import im.vector.matrix.android.api.util.JsonDict
 import im.vector.matrix.android.api.util.Optional
 import im.vector.matrix.android.api.util.toOptional
 import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
 import io.reactivex.Observable
 import io.reactivex.Single
 
@@ -123,7 +123,7 @@ class RxSession(private val session: Session) {
                 }
     }
 
-    fun liveAccountData(filter: List<String>): Observable<List<UserAccountData>> {
+    fun liveAccountData(filter: List<String>): Observable<List<UserAccountDataEvent>> {
         return session.getLiveAccountData(filter).asObservable()
                 .startWithCallable {
                     session.getAccountData(filter)
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
new file mode 100644
index 0000000000..4ea611b875
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
@@ -0,0 +1,272 @@
+/*
+ * 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.ssss
+
+import android.util.Base64
+import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import im.vector.matrix.android.InstrumentedTest
+import im.vector.matrix.android.api.MatrixCallback
+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.KeySigner
+import im.vector.matrix.android.api.session.securestorage.SSSSKeyCreationInfo
+import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
+import im.vector.matrix.android.api.util.Optional
+import im.vector.matrix.android.common.CommonTestHelper
+import im.vector.matrix.android.common.CryptoTestHelper
+import im.vector.matrix.android.common.SessionTestParams
+import im.vector.matrix.android.common.TestConstants
+import im.vector.matrix.android.common.TestMatrixCallback
+import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert
+import org.junit.Assert.fail
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import java.util.concurrent.CountDownLatch
+
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class QuadSTests : InstrumentedTest {
+
+    private val mTestHelper = CommonTestHelper(context())
+    private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
+
+    @Test
+    fun test_Generate4SKey() {
+
+        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+
+        val aliceLatch = CountDownLatch(1)
+
+        val quadS = aliceSession.sharedSecretStorageService
+
+        val emptyKeySigner = object : KeySigner {
+            override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
+                return null
+            }
+        }
+
+        var recoveryKey: String? = null
+
+        val TEST_KEY_ID = "my.test.Key"
+
+        quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
+                object : MatrixCallback<SSSSKeyCreationInfo> {
+
+                    override fun onSuccess(data: SSSSKeyCreationInfo) {
+                        recoveryKey = data.recoveryKey
+                        aliceLatch.countDown()
+                    }
+
+                    override fun onFailure(failure: Throwable) {
+                        Assert.fail("onFailure " + failure.localizedMessage)
+                        aliceLatch.countDown()
+                    }
+                })
+
+        mTestHelper.await(aliceLatch)
+
+        // Assert Account data is updated
+        val accountDataLock = CountDownLatch(1)
+        var accountData: UserAccountDataEvent? = null
+
+        val liveAccountData = runBlocking(Dispatchers.Main) {
+            aliceSession.getLiveAccountData("m.secret_storage.key.$TEST_KEY_ID")
+        }
+        val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
+            if (t?.getOrNull()?.type == "m.secret_storage.key.$TEST_KEY_ID") {
+                accountData = t.getOrNull()
+                accountDataLock.countDown()
+            }
+        }
+        GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
+
+        mTestHelper.await(accountDataLock)
+
+        Assert.assertNotNull("Key should be stored in account data", accountData)
+        val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
+        Assert.assertNotNull("Key Content cannot be parsed", parsed)
+        Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecureStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
+        Assert.assertEquals("Unexpected key name", "Test Key", parsed.name)
+        Assert.assertNull("Key was not generated from passphrase", parsed.passphrase)
+        Assert.assertNotNull("Pubkey should be defined", parsed.publicKey)
+
+        val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!)
+        DefaultSharedSecureStorage.withOlmDecryption { olmPkDecryption ->
+            val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
+            Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
+        }
+
+        // Set as default key
+        quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback<Unit> {})
+
+        var defaultKeyAccountData: UserAccountDataEvent? = null
+        val defaultDataLock = CountDownLatch(1)
+
+        val liveDefAccountData = runBlocking(Dispatchers.Main) {
+            aliceSession.getLiveAccountData(DefaultSharedSecureStorage.DEFAULT_KEY_ID)
+        }
+        val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
+            if (t?.getOrNull()?.type == DefaultSharedSecureStorage.DEFAULT_KEY_ID) {
+                defaultKeyAccountData = t.getOrNull()!!
+                defaultDataLock.countDown()
+            }
+        }
+        GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) }
+
+        mTestHelper.await(defaultDataLock)
+
+
+        Assert.assertNotNull(defaultKeyAccountData?.content)
+        Assert.assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
+
+
+        mTestHelper.signout(aliceSession)
+    }
+
+    @Test
+    fun test_StoreSecret() {
+        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+        val keyId = "My.Key"
+        val info = generatedSecret(aliceSession, keyId, true)
+
+        // Store a secret
+
+        val storeCountDownLatch = CountDownLatch(1)
+        val clearSecret = Base64.encodeToString("42".toByteArray(), Base64.NO_PADDING or Base64.NO_WRAP)
+        aliceSession.sharedSecretStorageService.storeSecret(
+                "secret.of.life",
+                clearSecret,
+                null, // default key
+                TestMatrixCallback(storeCountDownLatch)
+        )
+
+        val secretAccountData = assertAccountData(aliceSession,"secret.of.life" )
+
+        val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*,*>
+        Assert.assertNotNull("Element should be encrypted", encryptedContent)
+        Assert.assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
+
+        val secret = EncryptedSecretContent.fromJson(encryptedContent?.get(keyId))
+        Assert.assertNotNull(secret?.ciphertext)
+        Assert.assertNotNull(secret?.mac)
+        Assert.assertNotNull(secret?.ephemeral)
+
+        // Try to decrypt??
+
+        val keySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(info.recoveryKey)
+
+        var decryptedSecret: String? = null
+
+        val decryptCountDownLatch = CountDownLatch(1)
+        aliceSession.sharedSecretStorageService.getSecret("secret.of.life" ,
+                 null, //default key
+                keySpec!!,
+                null,
+                object : MatrixCallback<String> {
+                    override fun onFailure(failure: Throwable) {
+                        fail("Fail to decrypt -> " +failure.localizedMessage)
+                        decryptCountDownLatch.countDown()
+                    }
+
+                    override fun onSuccess(data: String) {
+                        decryptedSecret = data
+                        decryptCountDownLatch.countDown()
+                    }
+                }
+        )
+        mTestHelper.await(decryptCountDownLatch)
+
+
+        Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret)
+        mTestHelper.signout(aliceSession)
+
+    }
+
+    private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
+        val accountDataLock = CountDownLatch(1)
+        var accountData: UserAccountDataEvent? = null
+
+        val liveAccountData = runBlocking(Dispatchers.Main) {
+            session.getLiveAccountData(type)
+        }
+        val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
+            if (t?.getOrNull()?.type == type) {
+                accountData = t.getOrNull()
+                accountDataLock.countDown()
+            }
+        }
+        GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
+        mTestHelper.await(accountDataLock)
+
+        Assert.assertNotNull("Account Data type:$type should be found", accountData)
+
+        return accountData!!
+    }
+
+    private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
+
+        val quadS = session.sharedSecretStorageService
+
+        val emptyKeySigner = object : KeySigner {
+            override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
+                return null
+            }
+        }
+
+        var creationInfo: SSSSKeyCreationInfo? = null
+
+        val generateLatch = CountDownLatch(1)
+
+        quadS.generateKey(keyId, keyId, emptyKeySigner,
+                object : MatrixCallback<SSSSKeyCreationInfo> {
+
+                    override fun onSuccess(data: SSSSKeyCreationInfo) {
+                        creationInfo = data
+                        generateLatch.countDown()
+                    }
+
+                    override fun onFailure(failure: Throwable) {
+                        Assert.fail("onFailure " + failure.localizedMessage)
+                        generateLatch.countDown()
+                    }
+                })
+
+        mTestHelper.await(generateLatch)
+
+        Assert.assertNotNull(creationInfo)
+
+        assertAccountData(session, "m.secret_storage.key.$keyId")
+        if (asDefault) {
+            val setDefaultLatch = CountDownLatch(1)
+            quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
+            mTestHelper.await(setDefaultLatch)
+            assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID)
+        }
+
+        return creationInfo!!
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
index 5255d7c224..4167131c68 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
@@ -34,6 +34,7 @@ import im.vector.matrix.android.api.session.pushers.PushersService
 import im.vector.matrix.android.api.session.room.RoomDirectoryService
 import im.vector.matrix.android.api.session.room.RoomService
 import im.vector.matrix.android.api.session.securestorage.SecureStorageService
+import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
 import im.vector.matrix.android.api.session.signout.SignOutService
 import im.vector.matrix.android.api.session.sync.FilterService
 import im.vector.matrix.android.api.session.sync.SyncState
@@ -161,4 +162,6 @@ interface Session :
          */
         fun onGlobalError(globalError: GlobalError)
     }
+
+    val sharedSecretStorageService: SharedSecretStorageService
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
index 7a3b9f0171..a832921dc7 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
@@ -19,17 +19,17 @@ package im.vector.matrix.android.api.session.accountdata
 import androidx.lifecycle.LiveData
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.util.Optional
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
 
 interface AccountDataService {
 
-    fun getAccountData(type: String): UserAccountData?
+    fun getAccountData(type: String): UserAccountDataEvent?
 
-    fun getLiveAccountData(type: String): LiveData<Optional<UserAccountData>>
+    fun getLiveAccountData(type: String): LiveData<Optional<UserAccountDataEvent>>
 
-    fun getAccountData(filterType: List<String>): List<UserAccountData>
+    fun getAccountData(filterType: List<String>): List<UserAccountDataEvent>
 
-    fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountData>>
+    fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountDataEvent>>
 
     fun updateAccountData(type: String, data: Any, callback: MatrixCallback<Unit>? = null)
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt
new file mode 100644
index 0000000000..4c8b51c668
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.internal.di.MoshiProvider
+
+/**
+ * The account_data will have an encrypted property that is a map from key ID to an object.
+ * The algorithm from the m.secret_storage.key.[key ID] data for the given key defines how the other properties are interpreted,
+ * though it's expected that most encryption schemes would have ciphertext and mac properties,
+ * where the ciphertext property is the unpadded base64-encoded ciphertext, and the mac is used to ensure the integrity of the data.
+ */
+@JsonClass(generateAdapter = true)
+data class EncryptedSecretContent(
+        /** unpadded base64-encoded ciphertext */
+        @Json(name = "ciphertext") val ciphertext: String? = null,
+        @Json(name = "mac") val mac: String? = null,
+        @Json(name = "ephemeral") val ephemeral: String? = null
+) {
+    companion object {
+        /**
+         * Facility method to convert from object which must be comprised of maps, lists,
+         * strings, numbers, booleans and nulls.
+         */
+        fun fromJson(obj: Any?): EncryptedSecretContent? {
+            return MoshiProvider.providesMoshi()
+                    .adapter(EncryptedSecretContent::class.java)
+                    .fromJsonValue(obj)
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeyInfoResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeyInfoResult.kt
new file mode 100644
index 0000000000..940f5298ef
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeyInfoResult.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 KeyInfoResult {
+    data class Success(val keyInfo: KeyInfo) : KeyInfoResult()
+    data class Error(val error: SharedSecretStorageError) : KeyInfoResult()
+
+    fun isSuccess(): Boolean = this is Success
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeySigner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeySigner.kt
new file mode 100644
index 0000000000..2cd7a74f31
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/KeySigner.kt
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+interface KeySigner {
+    fun sign(canonicalJson: String): Map<String, Map<String, String>>?
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
new file mode 100644
index 0000000000..3c629290fc
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
@@ -0,0 +1,23 @@
+/*
+ * 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
+
+data class SSSSKeyCreationInfo (
+        val keyId: String = "",
+        var content: SecretStorageKeyContent?,
+        val recoveryKey: String = ""
+)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
new file mode 100644
index 0000000000..dcdba38d8e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
@@ -0,0 +1,67 @@
+/*
+ * 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
+
+import im.vector.matrix.android.api.listeners.ProgressListener
+import im.vector.matrix.android.internal.crypto.keysbackup.deriveKey
+import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
+
+/** Tag class */
+interface SSSSKeySpec
+
+data class Curve25519AesSha2KeySpec(
+        val privateKey: ByteArray
+) : SSSSKeySpec {
+
+    companion object {
+
+        fun fromPassphrase(passphrase: String, salt: String, iterations: Int, progressListener: ProgressListener?): Curve25519AesSha2KeySpec {
+            return Curve25519AesSha2KeySpec(
+                    privateKey = deriveKey(
+                            passphrase,
+                            salt,
+                            iterations,
+                            progressListener
+                    )
+            )
+        }
+
+        fun fromRecoveryKey(recoveryKey: String): Curve25519AesSha2KeySpec? {
+            return extractCurveKeyFromRecoveryKey(recoveryKey)?.let {
+                Curve25519AesSha2KeySpec(
+                        privateKey = it
+                )
+            }
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as Curve25519AesSha2KeySpec
+
+        if (!privateKey.contentEquals(other.privateKey)) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return privateKey.contentHashCode()
+    }
+}
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
new file mode 100644
index 0000000000..0aba3d700d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
@@ -0,0 +1,101 @@
+/*
+ * 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
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.internal.di.MoshiProvider
+import im.vector.matrix.android.internal.util.JsonCanonicalizer
+
+/**
+ *
+ * The contents of the account data for the key will include an algorithm property, which indicates the encryption algorithm used, as well as a name property,
+ * which is a human-readable name.
+ * The contents will be signed as signed JSON using the user's master cross-signing key. Other properties depend on the encryption algorithm.
+ *
+ *
+ * "content": {
+ *     "algorithm": "m.secret_storage.v1.curve25519-aes-sha2",
+ *     "passphrase": {
+ *         "algorithm": "m.pbkdf2",
+ *         "iterations": 500000,
+ *         "salt": "IrswcMWnYieBALCAOMBw9k93xSzlc2su"
+ *     },
+ *     "pubkey": "qql1q3IvBbwMU97zLnyh9HYW5x/zqTy5eoK1n+9fm1Y",
+ *     "signatures": {
+ *         "@valere35:matrix.org": {
+ *             "ed25519:nOUQYiH9L8uKp5JajqiQyv+Loa3+lsdil7UBverz/Ko": "QtePmwfUL7+SHYRJT/HaTgF7gUFog1E/wtUCt0qc5aB8N+Sz5iCOvQ0KtaFHQ5SJzsBlYH8k7ejoBc0RcnU7BA"
+ *         }
+ *     }
+ * }
+ */
+
+data class KeyInfo(
+        val id: String,
+        val content: SecretStorageKeyContent
+)
+
+@JsonClass(generateAdapter = true)
+data class SecretStorageKeyContent(
+        /** Currently support m.secret_storage.v1.curve25519-aes-sha2 */
+        @Json(name = "algorithm") val algorithm: String? = null,
+        @Json(name = "name") val name: String? = null,
+        @Json(name = "passphrase") val passphrase: SSSSPassphrase? = null,
+        @Json(name = "pubkey") val publicKey: String? = null,
+        @Json(name = "signatures")
+        var signatures: Map<String, Map<String, String>>? = null
+) {
+
+    private fun signalableJSONDictionary(): Map<String, Any> {
+        val map = HashMap<String, Any>()
+        algorithm?.let { map["algorithm"] = it }
+        name?.let { map["name"] = it }
+        publicKey?.let { map["pubkey"] = it }
+        passphrase?.let { ssspp ->
+            map["passphrase"] = mapOf(
+                    "algorithm" to ssspp.algorithm,
+                    "iterations" to ssspp.salt,
+                    "salt" to ssspp.salt
+            )
+        }
+        return map
+    }
+
+    fun canonicalSignable(): String {
+        return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
+    }
+
+    companion object {
+        /**
+         * Facility method to convert from object which must be comprised of maps, lists,
+         * strings, numbers, booleans and nulls.
+         */
+        fun fromJson(obj: Any?): SecretStorageKeyContent? {
+            return MoshiProvider.providesMoshi()
+                    .adapter(SecretStorageKeyContent::class.java)
+                    .fromJsonValue(obj)
+        }
+    }
+}
+
+@JsonClass(generateAdapter = true)
+data class SSSSPassphrase(
+        @Json(name = "algorithm") val algorithm: String?,
+        @Json(name = "iterations") val iterations: Int,
+        @Json(name = "salt") val salt: String?
+)
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
new file mode 100644
index 0000000000..1ff5cf12f3
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
@@ -0,0 +1,30 @@
+/*
+ * 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 SharedSecretStorageError(message: String?) : Throwable(message) {
+
+    data class UnknownSecret(val secretName: String) : SharedSecretStorageError("Unknown Secret $secretName")
+    data class UnknownKey(val keyId: String) : SharedSecretStorageError("Unknown key $keyId")
+    data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId")
+    data class UnsupportedAlgorithm(val algorithm: String) : SharedSecretStorageError("Unknown algorithm $algorithm")
+    data class SecretNotEncrypted(val secretName: String) : SharedSecretStorageError("Missing content for secret $secretName")
+    data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String) : SharedSecretStorageError("Missing content for secret $secretName with key $keyId")
+    object BadKeyFormat : SharedSecretStorageError("Bad Key Format")
+    object ParsingError : SharedSecretStorageError("parsing Error")
+    data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage)
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
index fa2042e506..9923aab606 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
@@ -17,6 +17,7 @@
 package im.vector.matrix.android.api.session.securestorage
 
 import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.listeners.ProgressListener
 
 /**
  * Some features may require clients to store encrypted data on the server so that it can be shared securely between clients.
@@ -39,7 +40,29 @@ interface SharedSecretStorageService {
      *
      * @return {string} the ID of the key
      */
-    fun addKey(algorithm: String, opts: Map<String, Any>, keyId: String, callback: MatrixCallback<String>)
+    fun generateKey(keyId: String,
+                    keyName: String,
+                    keySigner: KeySigner,
+                    callback: MatrixCallback<SSSSKeyCreationInfo>)
+
+    fun generateKeyWithPassphrase(keyId: String,
+                                  keyName: String,
+                                  passphrase: String,
+                                  keySigner: KeySigner,
+                                  progressListener: ProgressListener?,
+                                  callback: MatrixCallback<SSSSKeyCreationInfo>)
+
+    fun getKey(keyId: String): KeyInfoResult
+
+    /**
+     * A key can be marked as the "default" key by setting the user's account_data with event type m.secret_storage.default_key
+     * to an object that has the ID of the key as its key property.
+     * The default key will be used to encrypt all secrets that the user would expect to be available on all their clients.
+     * Unless the user specifies otherwise, clients will try to use the default key to decrypt secrets.
+     */
+    fun getDefaultKey(): KeyInfoResult
+
+    fun setDefaultKey(keyId: String, callback: MatrixCallback<Unit>)
 
     /**
      * Check whether we have a key with a given ID.
@@ -51,23 +74,29 @@ interface SharedSecretStorageService {
 
     /**
      * Store an encrypted secret on the server
+     * Clients MUST ensure that the key is trusted before using it to encrypt secrets.
      *
      * @param name The name of the secret
      * @param secret The secret contents.
-     * @param keys The IDs of the keys to use to encrypt the secret or null to use the default key.
+     * @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>)
 
+    /**
+     * Use this call to determine which SSSSKeySpec to use for requesting secret
+     */
+    fun getAlgorithmsForSecret(name: String): List<KeyInfoResult>
 
     /**
      * Get an encrypted secret from the shared storage
      *
      * @param name The name of the secret
-     * @param keyId The id of the key that should be used to decrypt
+     * @param keyId The id of the key that should be used to decrypt (null for default key)
      * @param privateKey the passphrase/secret
      *
      * @return The decrypted value
      */
-    fun getSecret(name: String, keyId: String, privateKey: String) : String
+    @Throws
 
+    fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback<String>)
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt
index 7906005046..595b55a7a6 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt
@@ -40,8 +40,28 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersi
 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature
 import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData
 import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
-import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.*
-import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
+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.crypto.keysbackup.tasks.CreateKeysBackupVersionTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteBackupTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteRoomSessionDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteRoomSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetKeysBackupLastVersionTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetKeysBackupVersionTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetRoomSessionDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetRoomSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreRoomSessionDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
+import im.vector.matrix.android.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
 import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
 import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
 import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt
index 344ba61277..2429c1e658 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupPassword.kt
@@ -83,10 +83,10 @@ fun retrievePrivateKeyWithPassword(password: String,
  * @return a private key.
  */
 @WorkerThread
-private fun deriveKey(password: String,
-                      salt: String,
-                      iterations: Int,
-                      progressListener: ProgressListener?): ByteArray {
+fun deriveKey(password: String,
+              salt: String,
+              iterations: Int,
+              progressListener: ProgressListener?): ByteArray {
     // Note: copied and adapted from MXMegolmExportEncryption
     val t0 = System.currentTimeMillis()
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
new file mode 100644
index 0000000000..a4cf02aeae
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
@@ -0,0 +1,366 @@
+/*
+ * 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.secrets
+
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.listeners.ProgressListener
+import im.vector.matrix.android.api.session.accountdata.AccountDataService
+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.KeyInfo
+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.SSSSKeyCreationInfo
+import im.vector.matrix.android.api.session.securestorage.SSSSKeySpec
+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.SharedSecretStorageError
+import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
+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.extensions.foldToCallback
+import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.matrix.olm.OlmPkDecryption
+import org.matrix.olm.OlmPkEncryption
+import org.matrix.olm.OlmPkMessage
+import javax.inject.Inject
+
+internal class DefaultSharedSecureStorage @Inject constructor(
+        private val accountDataService: AccountDataService,
+        private val coroutineDispatchers: MatrixCoroutineDispatchers,
+        private val cryptoCoroutineScope: CoroutineScope
+) : SharedSecretStorageService {
+
+    override fun generateKey(keyId: String,
+                             keyName: String,
+                             keySigner: KeySigner,
+                             callback: MatrixCallback<SSSSKeyCreationInfo>) {
+
+        cryptoCoroutineScope.launch(coroutineDispatchers.main) {
+            val pkDecryption = OlmPkDecryption()
+            val pubKey: String
+            val privateKey: ByteArray
+            try {
+                pubKey = pkDecryption.generateKey()
+                privateKey = pkDecryption.privateKey()
+            } catch (failure: Throwable) {
+                return@launch Unit.also {
+                    callback.onFailure(failure)
+                }
+            } finally {
+                pkDecryption.releaseDecryption()
+            }
+
+            val storageKeyContent = SecretStorageKeyContent(
+                    name = keyName,
+                    algorithm = ALGORITHM_CURVE25519_AES_SHA2,
+                    passphrase = null,
+                    publicKey = pubKey
+            )
+
+            val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
+                storageKeyContent.copy(
+                        signatures = it
+                )
+            } ?: storageKeyContent
+
+            accountDataService.updateAccountData(
+                    "$KEY_ID_BASE.$keyId",
+                    signedContent,
+                    object : MatrixCallback<Unit> {
+                        override fun onFailure(failure: Throwable) {
+                            callback.onFailure(failure)
+                        }
+
+                        override fun onSuccess(data: Unit) {
+                            callback.onSuccess(SSSSKeyCreationInfo(
+                                    keyId = keyId,
+                                    content = storageKeyContent,
+                                    recoveryKey = computeRecoveryKey(privateKey)
+                            ))
+                        }
+                    }
+            )
+        }
+    }
+
+    override fun generateKeyWithPassphrase(keyId: String,
+                                           keyName: String,
+                                           passphrase: String,
+                                           keySigner: KeySigner,
+                                           progressListener: ProgressListener?,
+                                           callback: MatrixCallback<SSSSKeyCreationInfo>) {
+        cryptoCoroutineScope.launch(coroutineDispatchers.main) {
+
+            val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
+
+            val pkDecryption = OlmPkDecryption()
+            val pubKey: String
+            try {
+                pubKey = pkDecryption.setPrivateKey(privatePart.privateKey)
+            } catch (failure: Throwable) {
+                return@launch Unit.also {
+                    callback.onFailure(failure)
+                }
+            } finally {
+                pkDecryption.releaseDecryption()
+            }
+
+            val storageKeyContent = SecretStorageKeyContent(
+                    algorithm = ALGORITHM_CURVE25519_AES_SHA2,
+                    passphrase = SSSSPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt),
+                    publicKey = pubKey
+            )
+
+            val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
+                storageKeyContent.copy(
+                        signatures = it
+                )
+            } ?: storageKeyContent
+
+            accountDataService.updateAccountData(
+                    "$KEY_ID_BASE.$keyId",
+                    signedContent,
+                    object : MatrixCallback<Unit> {
+                        override fun onFailure(failure: Throwable) {
+                            callback.onFailure(failure)
+                        }
+
+                        override fun onSuccess(data: Unit) {
+                            callback.onSuccess(SSSSKeyCreationInfo(
+                                    keyId = keyId,
+                                    content = storageKeyContent,
+                                    recoveryKey = computeRecoveryKey(privatePart.privateKey)
+                            ))
+                        }
+                    }
+            )
+
+        }
+    }
+
+    override fun hasKey(keyId: String): Boolean {
+        return accountDataService.getAccountData("$KEY_ID_BASE.$keyId") != null
+    }
+
+    override fun getKey(keyId: String): KeyInfoResult {
+        val accountData = accountDataService.getAccountData("$KEY_ID_BASE.$keyId")
+                ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(keyId))
+        return SecretStorageKeyContent.fromJson(accountData.content)?.let {
+            KeyInfoResult.Success(
+                    KeyInfo(id = keyId, content = it)
+            )
+        } ?: KeyInfoResult.Error(SharedSecretStorageError.UnknownAlgorithm(keyId))
+    }
+
+    override fun setDefaultKey(keyId: String, callback: MatrixCallback<Unit>) {
+        val existingKey = getKey(keyId)
+        if (existingKey is KeyInfoResult.Success) {
+            accountDataService.updateAccountData(DEFAULT_KEY_ID,
+                    mapOf("key" to keyId),
+                    callback
+            )
+        } else {
+            callback.onFailure(SharedSecretStorageError.UnknownKey(keyId))
+        }
+    }
+
+    override fun getDefaultKey(): KeyInfoResult {
+        val accountData = accountDataService.getAccountData(DEFAULT_KEY_ID)
+                ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(DEFAULT_KEY_ID))
+        val keyId = accountData.content["key"] as? String
+                ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(DEFAULT_KEY_ID))
+        return getKey(keyId)
+    }
+
+    override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) {
+
+        cryptoCoroutineScope.launch(coroutineDispatchers.main) {
+            val encryptedContents = HashMap<String, EncryptedSecretContent>()
+            try {
+
+                if (keys == null || keys.isEmpty()) {
+                    //use default key
+                    val key = getDefaultKey()
+                    when (key) {
+                        is KeyInfoResult.Success -> {
+                            if (key.keyInfo.content.algorithm == ALGORITHM_CURVE25519_AES_SHA2) {
+                                withOlmEncryption { olmEncrypt ->
+                                    olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
+                                    val encryptedResult = olmEncrypt.encrypt(secretBase64)
+                                    encryptedContents[key.keyInfo.id] = 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
+                        }
+                    }
+                } else {
+                    keys.forEach {
+                        val keyId = it
+                        // encrypt the content
+                        val key = getKey(keyId)
+                        when (key) {
+                            is KeyInfoResult.Success -> {
+                                if (key.keyInfo.content.algorithm == ALGORITHM_CURVE25519_AES_SHA2) {
+                                    withOlmEncryption { olmEncrypt ->
+                                        olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
+                                        val encryptedResult = 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(
+                        type = name,
+                        data = mapOf(
+                                "encrypted" to encryptedContents
+                        ),
+                        callback = callback
+                )
+            } catch (failure: Throwable) {
+                callback.onFailure(failure)
+            }
+
+        }
+
+        // Add default key
+    }
+
+    override fun getAlgorithmsForSecret(name: String): List<KeyInfoResult> {
+        val accountData = accountDataService.getAccountData(name)
+                ?: return listOf(KeyInfoResult.Error(SharedSecretStorageError.UnknownSecret(name)))
+        val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *>
+                ?: return listOf(KeyInfoResult.Error(SharedSecretStorageError.SecretNotEncrypted(name)))
+
+        val results = ArrayList<KeyInfoResult>()
+        encryptedContent.keys.forEach {
+            (it as? String)?.let { keyId ->
+                results.add(getKey(keyId))
+            }
+        }
+        return results
+    }
+
+    override fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback<String>) {
+        val accountData = accountDataService.getAccountData(name) ?: return Unit.also {
+            callback.onFailure(SharedSecretStorageError.UnknownSecret(name))
+        }
+        val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *> ?: return Unit.also {
+            callback.onFailure(SharedSecretStorageError.SecretNotEncrypted(name))
+        }
+        val key = keyId?.let { getKey(it) } as? KeyInfoResult.Success ?: getDefaultKey() as? KeyInfoResult.Success ?: return Unit.also {
+            callback.onFailure(SharedSecretStorageError.UnknownKey(name))
+        }
+
+        val encryptedForKey = encryptedContent[key.keyInfo.id] ?: return Unit.also {
+            callback.onFailure(SharedSecretStorageError.SecretNotEncryptedWithKey(name, key.keyInfo.id))
+        }
+
+        val secretContent = EncryptedSecretContent.fromJson(encryptedForKey)
+                ?: return Unit.also {
+                    callback.onFailure(SharedSecretStorageError.ParsingError)
+                }
+
+        val algorithm = key.keyInfo.content
+        if (ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
+            val keySpec = secretKey as? Curve25519AesSha2KeySpec ?: return Unit.also {
+                callback.onFailure(SharedSecretStorageError.BadKeyFormat)
+            }
+            cryptoCoroutineScope.launch(coroutineDispatchers.main) {
+                kotlin.runCatching {
+                    // decryt from recovery key
+                    val keyBytes = keySpec.privateKey
+                    val decryption = OlmPkDecryption()
+                    try {
+                        decryption.setPrivateKey(keyBytes)
+                        decryption.decrypt(OlmPkMessage().apply {
+                            mCipherText = secretContent.ciphertext
+                            mEphemeralKey = secretContent.ephemeral
+                            mMac = secretContent.mac
+                        })
+                    } catch (failure: Throwable) {
+                        throw failure
+                    } finally {
+                        decryption.releaseDecryption()
+                    }
+                }.foldToCallback(callback)
+            }
+        } else {
+            callback.onFailure(SharedSecretStorageError.UnsupportedAlgorithm(algorithm.algorithm ?: ""))
+        }
+    }
+
+    companion object {
+        const val KEY_ID_BASE = "m.secret_storage.key"
+        const val ENCRYPTED = "encrypted"
+        const val DEFAULT_KEY_ID = "m.secret_storage.default_key"
+
+        const val ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
+
+        fun withOlmEncryption(block: (OlmPkEncryption) -> Unit) {
+            val olmPkEncryption = OlmPkEncryption()
+            try {
+                block(olmPkEncryption)
+            } catch (failure: Throwable) {
+                throw failure
+            } finally {
+                olmPkEncryption.releaseEncryption()
+            }
+        }
+
+        fun withOlmDecryption(block: (OlmPkDecryption) -> Unit) {
+            val olmPkDecryption = OlmPkDecryption()
+            try {
+                block(olmPkDecryption)
+            } catch (failure: Throwable) {
+                throw failure
+            } finally {
+                olmPkDecryption.releaseDecryption()
+            }
+        }
+    }
+}
+
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/SecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/SecretStorageService.kt
deleted file mode 100644
index 33306104eb..0000000000
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/SecretStorageService.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.secrets
-
-import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
-
-internal class DefaultSharedSecureStorage : SharedSecretStorageService {
-
-    override fun addKey(algorithm: String, opts: Map<String, Any>, keyId: String, callback: MatrixCallback<String>) {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
-    override fun hasKey(keyId: String): Boolean {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
-    override fun storeSecret(name: String, secretBase64: String, keys: List<String>?, callback: MatrixCallback<Unit>) {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
-    override fun getSecret(name: String, keyId: String, privateKey: String): String {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
index c19c686329..cd4e9abbc1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
@@ -23,7 +23,7 @@ import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataBreadcrumbs
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
 
@@ -31,7 +31,7 @@ object MoshiProvider {
 
     private val moshi: Moshi = Moshi.Builder()
             .add(UriMoshiAdapter())
-            .add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
+            .add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataEvent::class.java)
                     .registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
                     .registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST)
                     .registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
index afe37c1c41..46264ceb85 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
@@ -38,6 +38,7 @@ import im.vector.matrix.android.api.session.pushers.PushersService
 import im.vector.matrix.android.api.session.room.RoomDirectoryService
 import im.vector.matrix.android.api.session.room.RoomService
 import im.vector.matrix.android.api.session.securestorage.SecureStorageService
+import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
 import im.vector.matrix.android.api.session.signout.SignOutService
 import im.vector.matrix.android.api.session.sync.FilterService
 import im.vector.matrix.android.api.session.sync.SyncState
@@ -93,6 +94,7 @@ internal class DefaultSession @Inject constructor(
         private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
         private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
         private val accountDataService: Lazy<AccountDataService>,
+        private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
         private val shieldTrustUpdater: ShieldTrustUpdater)
     : Session,
         RoomService by roomService.get(),
@@ -111,6 +113,9 @@ internal class DefaultSession @Inject constructor(
         ProfileService by profileService.get(),
         AccountDataService by accountDataService.get() {
 
+    override val sharedSecretStorageService: SharedSecretStorageService
+        get() = _sharedSecretStorageService.get()
+
     private var isOpen = false
 
     private var syncThread: SyncThread? = null
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
index 636e61c93f..02d8a35009 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
@@ -35,6 +35,8 @@ import im.vector.matrix.android.api.session.Session
 import im.vector.matrix.android.api.session.accountdata.AccountDataService
 import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
 import im.vector.matrix.android.api.session.securestorage.SecureStorageService
+import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
+import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage
 import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver
 import im.vector.matrix.android.internal.database.LiveEntityObserver
 import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
@@ -268,4 +270,7 @@ internal abstract class SessionModule {
 
     @Binds
     abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService
+
+    @Binds
+    abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecureStorage): SharedSecretStorageService
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt
similarity index 96%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt
index d965d2ffee..a4ba0fc91a 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataEvent.kt
@@ -20,7 +20,7 @@ import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
-data class UserAccountDataFallback(
+data class UserAccountDataEvent(
         @Json(name = "type") override val type: String,
         @Json(name = "content") val content: Map<String, Any>
 ) : UserAccountData()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
index 2aeef3cc0d..0bb57f0dae 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
@@ -28,8 +28,7 @@ import im.vector.matrix.android.internal.database.model.UserAccountDataEntity
 import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields
 import im.vector.matrix.android.internal.di.MoshiProvider
 import im.vector.matrix.android.internal.di.SessionId
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.configureWith
 import javax.inject.Inject
@@ -44,17 +43,17 @@ internal class DefaultAccountDataService @Inject constructor(
     private val moshi = MoshiProvider.providesMoshi()
     private val adapter = moshi.adapter<Map<String, Any>>(JSON_DICT_PARAMETERIZED_TYPE)
 
-    override fun getAccountData(type: String): UserAccountData? {
+    override fun getAccountData(type: String): UserAccountDataEvent? {
         return getAccountData(listOf(type)).firstOrNull()
     }
 
-    override fun getLiveAccountData(type: String): LiveData<Optional<UserAccountData>> {
+    override fun getLiveAccountData(type: String): LiveData<Optional<UserAccountDataEvent>> {
         return Transformations.map(getLiveAccountData(listOf(type))) {
             it.firstOrNull()?.toOptional()
         }
     }
 
-    override fun getAccountData(filterType: List<String>): List<UserAccountData> {
+    override fun getAccountData(filterType: List<String>): List<UserAccountDataEvent> {
         return monarchy.fetchAllCopiedSync { realm ->
             realm.where(UserAccountDataEntity::class.java)
                     .apply {
@@ -64,7 +63,7 @@ internal class DefaultAccountDataService @Inject constructor(
                     }
         }?.mapNotNull { entity ->
             entity.type?.let { type ->
-                UserAccountDataFallback(
+                UserAccountDataEvent(
                         type = type,
                         content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
                 )
@@ -72,7 +71,7 @@ internal class DefaultAccountDataService @Inject constructor(
         } ?: emptyList()
     }
 
-    override fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountData>> {
+    override fun getLiveAccountData(filterType: List<String>): LiveData<List<UserAccountDataEvent>> {
         return monarchy.findAllMappedWithChanges({ realm ->
             realm.where(UserAccountDataEntity::class.java)
                     .apply {
@@ -81,7 +80,7 @@ internal class DefaultAccountDataService @Inject constructor(
                         }
                     }
         }, { entity ->
-            UserAccountDataFallback(
+            UserAccountDataEvent(
                     type = entity.type ?: "",
                     content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
             )
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
index 5b7b090dcd..a7d5d82fb1 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
@@ -22,7 +22,7 @@ import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
 import im.vector.matrix.android.internal.di.MoshiProvider
 import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
-import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.configureWith
 import im.vector.riotx.core.platform.VectorBaseActivity
@@ -55,9 +55,9 @@ class AccountDataFragment @Inject constructor(
     }
 
     override fun didTap(data: UserAccountData) {
-        val fb = data as? UserAccountDataFallback ?: return
+        val fb = data as? UserAccountDataEvent ?: return
         val jsonString = MoshiProvider.providesMoshi()
-                .adapter(UserAccountDataFallback::class.java)
+                .adapter(UserAccountDataEvent::class.java)
                 .toJson(fb)
         JsonViewerBottomSheetDialog.newInstance(jsonString)
                 .show(childFragmentManager, "JSON_VIEWER")