Some more cleanup on the model used to download and to upload keys

This commit is contained in:
Benoit Marty 2020-06-19 23:44:16 +02:00
parent 1fb2569a39
commit 4125baf066
12 changed files with 157 additions and 98 deletions

View file

@ -15,9 +15,9 @@
*/ */
package im.vector.matrix.android.internal.crypto.model package im.vector.matrix.android.internal.crypto.model
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
import im.vector.matrix.android.internal.crypto.model.rest.UnsignedDeviceInfo
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity
@ -27,7 +27,7 @@ data class CryptoDeviceInfo(
var algorithms: List<String>? = null, var algorithms: List<String>? = null,
override val keys: Map<String, String>? = null, override val keys: Map<String, String>? = null,
override val signatures: Map<String, Map<String, String>>? = null, override val signatures: Map<String, Map<String, String>>? = null,
val unsigned: JsonDict? = null, val unsigned: UnsignedDeviceInfo? = null,
var trustLevel: DeviceTrustLevel? = null, var trustLevel: DeviceTrustLevel? = null,
var isBlocked: Boolean = false, var isBlocked: Boolean = false,
val firstTimeSeenLocalTs: Long? = null val firstTimeSeenLocalTs: Long? = null
@ -61,7 +61,7 @@ data class CryptoDeviceInfo(
* @return the display name * @return the display name
*/ */
fun displayName(): String? { fun displayName(): String? {
return unsigned?.get("device_display_name") as? String return unsigned?.deviceDisplayName
} }
override fun signalableJSONDictionary(): Map<String, Any> { override fun signalableJSONDictionary(): Map<String, Any> {
@ -74,7 +74,7 @@ data class CryptoDeviceInfo(
} }
} }
internal fun CryptoDeviceInfo.toRest(): RestDeviceInfo { internal fun CryptoDeviceInfo.toRest(): DeviceKeys {
return CryptoInfoMapper.map(this) return CryptoInfoMapper.map(this)
} }

View file

@ -15,15 +15,16 @@
*/ */
package im.vector.matrix.android.internal.crypto.model package im.vector.matrix.android.internal.crypto.model
import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeysWithUnsigned
import im.vector.matrix.android.internal.crypto.model.rest.RestKeyInfo import im.vector.matrix.android.internal.crypto.model.rest.RestKeyInfo
internal object CryptoInfoMapper { internal object CryptoInfoMapper {
fun map(restDeviceInfo: RestDeviceInfo): CryptoDeviceInfo { fun map(restDeviceInfo: DeviceKeysWithUnsigned): CryptoDeviceInfo {
return CryptoDeviceInfo( return CryptoDeviceInfo(
deviceId = restDeviceInfo.deviceId, deviceId = restDeviceInfo.deviceId ?: "",
userId = restDeviceInfo.userId, userId = restDeviceInfo.userId ?: "",
algorithms = restDeviceInfo.algorithms, algorithms = restDeviceInfo.algorithms,
keys = restDeviceInfo.keys, keys = restDeviceInfo.keys,
signatures = restDeviceInfo.signatures, signatures = restDeviceInfo.signatures,
@ -32,13 +33,12 @@ internal object CryptoInfoMapper {
) )
} }
fun map(cryptoDeviceInfo: CryptoDeviceInfo): RestDeviceInfo { fun map(cryptoDeviceInfo: CryptoDeviceInfo): DeviceKeys {
return RestDeviceInfo( return DeviceKeys(
deviceId = cryptoDeviceInfo.deviceId, deviceId = cryptoDeviceInfo.deviceId,
algorithms = cryptoDeviceInfo.algorithms, algorithms = cryptoDeviceInfo.algorithms,
keys = cryptoDeviceInfo.keys, keys = cryptoDeviceInfo.keys,
signatures = cryptoDeviceInfo.signatures, signatures = cryptoDeviceInfo.signatures,
unsigned = cryptoDeviceInfo.unsigned,
userId = cryptoDeviceInfo.userId userId = cryptoDeviceInfo.userId
) )
} }

View file

@ -21,21 +21,36 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class DeviceKeys( data class DeviceKeys(
/**
* Required. The ID of the user the device belongs to. Must match the user ID used when logging in.
*/
@Json(name = "user_id") @Json(name = "user_id")
val userId: String?, val userId: String,
/**
* Required. The ID of the device these keys belong to. Must match the device ID used when logging in.
*/
@Json(name = "device_id") @Json(name = "device_id")
val deviceId: String?, val deviceId: String,
/**
* Required. The encryption algorithms supported by this device.
*/
@Json(name = "algorithms") @Json(name = "algorithms")
val algorithms: List<String>?, val algorithms: List<String>?,
/**
* Required. Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>.
* The keys themselves should be encoded as specified by the key algorithm.
*/
@Json(name = "keys") @Json(name = "keys")
val keys: Map<String, String>?, val keys: Map<String, String>?,
/**
* Required. Signatures for the device key object. A map from user ID, to a map from <algorithm>:<device_id> to the signature.
* The signature is calculated using the process described at https://matrix.org/docs/spec/appendices.html#signing-json.
*/
@Json(name = "signatures") @Json(name = "signatures")
val signatures: Map<String, Map<String, String>>?, val signatures: Map<String, Map<String, String>>?
@Json(name = "usage")
val usage: List<String>? = null
) )

View file

@ -0,0 +1,62 @@
/*
* 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.model.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class DeviceKeysWithUnsigned(
/**
* Required. The ID of the user the device belongs to. Must match the user ID used when logging in.
*/
@Json(name = "user_id")
val userId: String?,
/**
* Required. The ID of the device these keys belong to. Must match the device ID used when logging in.
*/
@Json(name = "device_id")
val deviceId: String?,
/**
* Required. The encryption algorithms supported by this device.
*/
@Json(name = "algorithms")
val algorithms: List<String>?,
/**
* Required. Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>.
* The keys themselves should be encoded as specified by the key algorithm.
*/
@Json(name = "keys")
val keys: Map<String, String>?,
/**
* Required. Signatures for the device key object. A map from user ID, to a map from <algorithm>:<device_id> to the signature.
* The signature is calculated using the process described at https://matrix.org/docs/spec/appendices.html#signing-json.
*/
@Json(name = "signatures")
val signatures: Map<String, Map<String, String>>?,
/**
* Additional data added to the device key information by intermediate servers, and not covered by the signatures.
*/
@Json(name = "unsigned")
val unsigned: UnsignedDeviceInfo? = null
)

View file

@ -30,16 +30,18 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class KeysQueryResponse( internal data class KeysQueryResponse(
/** /**
* The device keys per devices per users. * Information on the queried devices. A map from user ID, to a map from device ID to device information.
* Map from userId to map from deviceId to MXDeviceInfo * For each device, the information returned will be the same as uploaded via /keys/upload, with the addition of an unsigned property.
* TODO Use MXUsersDevicesMap?
*/ */
@Json(name = "device_keys") @Json(name = "device_keys")
val deviceKeys: Map<String, Map<String, RestDeviceInfo>>? = null, val deviceKeys: Map<String, Map<String, DeviceKeysWithUnsigned>>? = null,
/** /**
* The failures sorted by homeservers. TODO Bad comment ? * If any remote homeservers could not be reached, they are recorded here. The names of the
* TODO Use MXUsersDevicesMap? * properties are the names of the unreachable servers.
*
* If the homeserver could be reached, but the user or device was unknown, no failure is recorded.
* Instead, the corresponding user or device is missing from the device_keys result.
*/ */
val failures: Map<String, Map<String, Any>>? = null, val failures: Map<String, Map<String, Any>>? = null,

View file

@ -20,11 +20,25 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
/**
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-upload
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class KeysUploadBody( internal data class KeysUploadBody(
/**
* Identity keys for the device.
*
* May be absent if no new identity keys are required.
*/
@Json(name = "device_keys") @Json(name = "device_keys")
val deviceKeys: RestDeviceInfo? = null, val deviceKeys: DeviceKeys? = null,
/**
* One-time public keys for "pre-key" messages. The names of the properties should be in the
* format <algorithm>:<key_id>. The format of the key is determined by the key algorithm.
*
* May be absent if no new one-time keys are required.
*/
@Json(name = "one_time_keys") @Json(name = "one_time_keys")
val oneTimeKeys: JsonDict? = null val oneTimeKeys: JsonDict? = null
) )

View file

@ -24,7 +24,8 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class KeysUploadResponse( internal data class KeysUploadResponse(
/** /**
* The count per algorithm as returned by the home server: a map (algorithm to count). * Required. For each key algorithm, the number of unclaimed one-time keys
* of that type currently held on the server for this device.
*/ */
@Json(name = "one_time_key_counts") @Json(name = "one_time_key_counts")
val oneTimeKeyCounts: Map<String, Int>? = null val oneTimeKeyCounts: Map<String, Int>? = null

View file

@ -1,60 +0,0 @@
/*
* Copyright 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.model.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict
@JsonClass(generateAdapter = true)
internal data class RestDeviceInfo(
/**
* The id of this device.
*/
@Json(name = "device_id")
val deviceId: String,
/**
* the user id
*/
@Json(name = "user_id")
val userId: String,
/**
* The list of algorithms supported by this device.
*/
@Json(name = "algorithms")
val algorithms: List<String>? = null,
/**
* A map from "<key type>:<deviceId>" to "<base64-encoded key>".
*/
@Json(name = "keys")
val keys: Map<String, String>? = null,
/**
* The signature of this MXDeviceInfo.
* A map from "<userId>" to a map from "<key type>:<deviceId>" to "<signature>"
*/
@Json(name = "signatures")
val signatures: Map<String, Map<String, String>>? = null,
/*
* Additional data from the home server.
*/
@Json(name = "unsigned")
val unsigned: JsonDict? = null
)

View file

@ -27,6 +27,7 @@ internal data class RestKeyInfo(
*/ */
@Json(name = "user_id") @Json(name = "user_id")
val userId: String, val userId: String,
/** /**
* Allowed uses for the key. * Allowed uses for the key.
* Must contain "master" for master keys, "self_signing" for self-signing keys, and "user_signing" for user-signing keys. * Must contain "master" for master keys, "self_signing" for self-signing keys, and "user_signing" for user-signing keys.

View file

@ -0,0 +1,29 @@
/*
* 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.model.rest
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class UnsignedDeviceInfo(
/**
* The display name which the user set on the device.
*/
@Json(name = "device_display_name")
val deviceDisplayName: String? = null
)

View file

@ -20,6 +20,7 @@ import com.squareup.moshi.Types
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.UnsignedDeviceInfo
import im.vector.matrix.android.internal.di.SerializeNulls import im.vector.matrix.android.internal.di.SerializeNulls
import timber.log.Timber import timber.log.Timber
@ -57,7 +58,8 @@ object CryptoMapper {
locallyVerified = it.locallyVerified locallyVerified = it.locallyVerified
) )
}, },
unsignedMapJson = mapMigrationAdapter.toJson(deviceInfo.unsigned) // We store the device name if present now
unsignedMapJson = deviceInfo.unsigned?.deviceDisplayName
) )
} }
@ -69,14 +71,7 @@ object CryptoMapper {
trustLevel = deviceInfoEntity.trustLevelEntity?.let { trustLevel = deviceInfoEntity.trustLevelEntity?.let {
DeviceTrustLevel(it.crossSignedVerified ?: false, it.locallyVerified) DeviceTrustLevel(it.crossSignedVerified ?: false, it.locallyVerified)
}, },
unsigned = deviceInfoEntity.unsignedMapJson?.let { unsigned = deviceInfoEntity.unsignedMapJson?.let { UnsignedDeviceInfo(deviceDisplayName = it) },
try {
mapMigrationAdapter.fromJson(it)
} catch (failure: Throwable) {
Timber.e(failure)
null
}
},
signatures = deviceInfoEntity.signatureMapJson?.let { signatures = deviceInfoEntity.signatureMapJson?.let {
try { try {
mapOfStringMigrationAdapter.fromJson(it) mapOfStringMigrationAdapter.fromJson(it)

View file

@ -18,9 +18,9 @@ package im.vector.matrix.android.internal.crypto.tasks
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.api.CryptoApi
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.model.rest.RestDeviceInfo
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -30,7 +30,7 @@ import javax.inject.Inject
internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadResponse> { internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadResponse> {
data class Params( data class Params(
// the device keys to send. // the device keys to send.
val deviceKeys: RestDeviceInfo?, val deviceKeys: DeviceKeys?,
// the one-time keys to send. // the one-time keys to send.
val oneTimeKeys: JsonDict? val oneTimeKeys: JsonDict?
) )