QrCode: WIP
This commit is contained in:
parent
39e746413a
commit
8659216955
|
@ -23,6 +23,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
|||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.QrCodeVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationService
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction
|
||||
|
@ -669,59 +670,14 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
var myGeneratedSharedSecret: String? = null
|
||||
val qrCodeText = readyReq.methods
|
||||
val qrCodeData = readyReq.methods
|
||||
// Check if other user is able to scan QR code
|
||||
?.takeIf { it.contains(VERIFICATION_METHOD_QR_CODE_SCAN) }
|
||||
?.let {
|
||||
// Build the QR code URL
|
||||
val requestEventId = existingRequest.transactionId ?: run {
|
||||
Timber.w("Unknown requestEventId")
|
||||
return@let null
|
||||
}
|
||||
|
||||
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||
?.masterKey()
|
||||
?.unpaddedBase64PublicKey
|
||||
?: run {
|
||||
Timber.w("Unable to get my master key")
|
||||
return@let null
|
||||
}
|
||||
|
||||
// TODO Force download?
|
||||
val otherUserMasterKey = crossSigningService.getUserCrossSigningKeys(existingRequest.otherUserId)
|
||||
?.masterKey()
|
||||
?.unpaddedBase64PublicKey
|
||||
?: run {
|
||||
Timber.w("Unable to get other user master key")
|
||||
return@let null
|
||||
}
|
||||
|
||||
val myDeviceId = deviceId
|
||||
?: run {
|
||||
Timber.w("Unable to get my deviceId")
|
||||
return@let null
|
||||
}
|
||||
|
||||
// TODO I'm pretty sure it's the correct key to put here
|
||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()!!
|
||||
|
||||
val generatedSharedSecret = generateSharedSecret()
|
||||
.also { myGeneratedSharedSecret = it }
|
||||
QrCodeData(
|
||||
userId = userId,
|
||||
requestEventId = requestEventId,
|
||||
action = QrCodeData.ACTION_VERIFY,
|
||||
keys = hashMapOf(
|
||||
myMasterKey to myMasterKey,
|
||||
myDeviceId to myDeviceKey
|
||||
),
|
||||
sharedSecret = generatedSharedSecret,
|
||||
otherUserKey = otherUserMasterKey
|
||||
).toUrl()
|
||||
createQrCodeData(existingRequest.transactionId, existingRequest.otherUserId)
|
||||
}
|
||||
|
||||
if (qrCodeText != null) {
|
||||
if (qrCodeData != null) {
|
||||
// Create the pending transaction
|
||||
val tx = DefaultQrCodeVerificationTransaction(
|
||||
readyReq.transactionID!!,
|
||||
|
@ -729,8 +685,8 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
readyReq.fromDevice,
|
||||
crossSigningService,
|
||||
cryptoStore,
|
||||
myGeneratedSharedSecret!!,
|
||||
qrCodeText,
|
||||
qrCodeData.sharedSecret,
|
||||
qrCodeData.toUrl(),
|
||||
deviceId ?: "",
|
||||
false)
|
||||
|
||||
|
@ -744,6 +700,51 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
))
|
||||
}
|
||||
|
||||
private fun createQrCodeData(transactionId: String?, otherUserId: String): QrCodeData? {
|
||||
// Build the QR code URL
|
||||
val requestEventId = transactionId ?: run {
|
||||
Timber.w("## Unknown requestEventId")
|
||||
return null
|
||||
}
|
||||
|
||||
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||
?.masterKey()
|
||||
?.unpaddedBase64PublicKey
|
||||
?: run {
|
||||
Timber.w("## Unable to get my master key")
|
||||
return null
|
||||
}
|
||||
|
||||
val otherUserMasterKey = crossSigningService.getUserCrossSigningKeys(otherUserId)
|
||||
?.masterKey()
|
||||
?.unpaddedBase64PublicKey
|
||||
?: run {
|
||||
Timber.w("## Unable to get other user master key")
|
||||
return null
|
||||
}
|
||||
|
||||
val myDeviceId = deviceId
|
||||
?: run {
|
||||
Timber.w("## Unable to get my deviceId")
|
||||
return null
|
||||
}
|
||||
|
||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()!!
|
||||
|
||||
val generatedSharedSecret = generateSharedSecret()
|
||||
return QrCodeData(
|
||||
userId = userId,
|
||||
requestEventId = requestEventId,
|
||||
action = QrCodeData.ACTION_VERIFY,
|
||||
keys = hashMapOf(
|
||||
myMasterKey to myMasterKey,
|
||||
myDeviceId to myDeviceKey
|
||||
),
|
||||
sharedSecret = generatedSharedSecret,
|
||||
otherUserKey = otherUserMasterKey
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleDoneReceived(senderId: String, doneInfo: VerificationInfo) {
|
||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionID }
|
||||
if (existingRequest == null) {
|
||||
|
@ -953,7 +954,13 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
if (existingRequest != null) {
|
||||
// we need to send a ready event, with matching methods
|
||||
val transport = verificationTransportRoomMessageFactory.createTransport(roomId, null)
|
||||
val computedMethods = computeReadyMethods(existingRequest.requestInfo?.methods, methods)
|
||||
val computedMethods = computeReadyMethods(
|
||||
transactionId,
|
||||
otherUserId,
|
||||
existingRequest.requestInfo?.fromDevice ?: "",
|
||||
roomId,
|
||||
existingRequest.requestInfo?.methods,
|
||||
methods)
|
||||
if (methods.isNullOrEmpty()) {
|
||||
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||
// TODO buttons should not be shown in this case?
|
||||
|
@ -976,7 +983,13 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun computeReadyMethods(otherUserMethods: List<String>?, methods: List<VerificationMethod>): List<String> {
|
||||
private fun computeReadyMethods(
|
||||
transactionId: String,
|
||||
otherUserId: String,
|
||||
otherDeviceId: String,
|
||||
roomId: String,
|
||||
otherUserMethods: List<String>?,
|
||||
methods: List<VerificationMethod>): List<String> {
|
||||
if (otherUserMethods.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
@ -985,17 +998,42 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
|
||||
if (VERIFICATION_METHOD_SAS in otherUserMethods && VerificationMethod.SAS in methods) {
|
||||
// Other can do SAS and so do I
|
||||
result + VERIFICATION_METHOD_SAS
|
||||
result.add(VERIFICATION_METHOD_SAS)
|
||||
}
|
||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in methods) {
|
||||
// Other can Scan and I can show QR code
|
||||
result + VERIFICATION_METHOD_QR_CODE_SHOW
|
||||
result + VERIFICATION_METHOD_RECIPROCATE
|
||||
}
|
||||
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in methods) {
|
||||
// Other can show and I can scan QR code
|
||||
result + VERIFICATION_METHOD_QR_CODE_SCAN
|
||||
result + VERIFICATION_METHOD_RECIPROCATE
|
||||
|
||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods || VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods) {
|
||||
// Other user want to verify using QR code. Cross-signing has to be setup
|
||||
val qrCodeData = createQrCodeData(transactionId, otherUserId)
|
||||
|
||||
if (qrCodeData != null) {
|
||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in methods) {
|
||||
// Other can Scan and I can show QR code
|
||||
result.add(VERIFICATION_METHOD_QR_CODE_SHOW)
|
||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
|
||||
// Create the pending request, to display the QR code
|
||||
// Create the pending transaction
|
||||
val tx = DefaultQrCodeVerificationTransaction(
|
||||
transactionId,
|
||||
otherUserId,
|
||||
otherDeviceId,
|
||||
crossSigningService,
|
||||
cryptoStore,
|
||||
qrCodeData.sharedSecret,
|
||||
qrCodeData.toUrl(),
|
||||
deviceId ?: "",
|
||||
false)
|
||||
|
||||
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
|
||||
|
||||
addTransaction(tx)
|
||||
}
|
||||
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in methods) {
|
||||
// Other can show and I can scan QR code
|
||||
result.add(VERIFICATION_METHOD_QR_CODE_SCAN)
|
||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toList()
|
||||
|
@ -1024,5 +1062,13 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
// remove
|
||||
this.removeTransaction(tx.otherUserId, tx.transactionId)
|
||||
}
|
||||
if (tx is QrCodeVerificationTransaction
|
||||
&& (tx.state == VerificationTxState.Cancelled
|
||||
|| tx.state == VerificationTxState.OnCancelled
|
||||
|| tx.state == VerificationTxState.Verified)
|
||||
) {
|
||||
// remove
|
||||
this.removeTransaction(tx.otherUserId, tx.transactionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,5 +152,10 @@ internal class DefaultQrCodeVerificationTransaction(
|
|||
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
||||
}
|
||||
})
|
||||
|
||||
// TODO Sign devices
|
||||
|
||||
}
|
||||
|
||||
// TODO Send the done event
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
it.otherUserMxItem?.let { matrixItem ->
|
||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||
|
||||
if (it.transactionState == VerificationTxState.Verified) {
|
||||
if (it.sasTransactionState == VerificationTxState.Verified || it.qrTransactionState == VerificationTxState.Verified) {
|
||||
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
|
||||
otherUserShield.isVisible = true
|
||||
} else {
|
||||
|
@ -108,8 +108,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
// Did the request result in a SAS transaction?
|
||||
if (it.transactionState != null) {
|
||||
when (it.transactionState) {
|
||||
if (it.sasTransactionState != null) {
|
||||
when (it.sasTransactionState) {
|
||||
VerificationTxState.None,
|
||||
VerificationTxState.SendingStart,
|
||||
VerificationTxState.Started,
|
||||
|
@ -138,7 +138,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
VerificationTxState.OnCancelled -> {
|
||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(
|
||||
it.transactionState == VerificationTxState.Verified,
|
||||
it.sasTransactionState == VerificationTxState.Verified,
|
||||
it.cancelCode?.value))
|
||||
})
|
||||
}
|
||||
|
@ -147,6 +147,19 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
return@withState
|
||||
}
|
||||
|
||||
when (it.qrTransactionState) {
|
||||
VerificationTxState.Verified,
|
||||
VerificationTxState.Cancelled,
|
||||
VerificationTxState.OnCancelled -> {
|
||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(
|
||||
it.qrTransactionState == VerificationTxState.Verified,
|
||||
it.cancelCode?.value))
|
||||
})
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
// At this point there is no transaction for this request
|
||||
|
||||
// Transaction has not yet started
|
||||
|
|
|
@ -52,7 +52,8 @@ data class VerificationBottomSheetViewState(
|
|||
val roomId: String? = null,
|
||||
val pendingRequest: Async<PendingVerificationRequest> = Uninitialized,
|
||||
val pendingLocalId: String? = null,
|
||||
val transactionState: VerificationTxState? = null,
|
||||
val sasTransactionState: VerificationTxState? = null,
|
||||
val qrTransactionState: VerificationTxState? = null,
|
||||
val transactionId: String? = null,
|
||||
val cancelCode: CancelCode? = null
|
||||
) : MvRxState
|
||||
|
@ -94,12 +95,17 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
val pr = session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
||||
|
||||
val sasTx = (pr?.transactionId ?: args.verificationId)?.let {
|
||||
session.getVerificationService().getExistingTransaction(args.otherUserId, it)
|
||||
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction
|
||||
}
|
||||
|
||||
val qrTx = (pr?.transactionId ?: args.verificationId)?.let {
|
||||
session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
|
||||
}
|
||||
|
||||
return fragment.verificationViewModelFactory.create(VerificationBottomSheetViewState(
|
||||
otherUserMxItem = userItem?.toMatrixItem(),
|
||||
transactionState = sasTx?.state,
|
||||
sasTransactionState = sasTx?.state,
|
||||
qrTransactionState = qrTx?.state,
|
||||
transactionId = args.verificationId,
|
||||
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
|
||||
roomId = args.roomId)
|
||||
|
@ -192,13 +198,28 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
|||
}
|
||||
|
||||
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
|
||||
if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) {
|
||||
// A SAS tx has been started following this request
|
||||
setState {
|
||||
copy(
|
||||
transactionState = tx.state,
|
||||
cancelCode = tx.cancelledReason
|
||||
)
|
||||
when (tx) {
|
||||
is SasVerificationTransaction -> {
|
||||
if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) {
|
||||
// A SAS tx has been started following this request
|
||||
setState {
|
||||
copy(
|
||||
sasTransactionState = tx.state,
|
||||
cancelCode = tx.cancelledReason
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is QrCodeVerificationTransaction -> {
|
||||
if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) {
|
||||
// A SAS tx has been started following this request
|
||||
setState {
|
||||
copy(
|
||||
qrTransactionState = tx.state,
|
||||
cancelCode = tx.cancelledReason
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,10 +98,14 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
|||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||
val pvr = session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
|
||||
|
||||
// Get the QR code now, because transaction is already created, so transactionCreated() will not be called
|
||||
val qrCodeVerificationTransaction = session.getVerificationService().getExistingTransaction(args.otherUserId, args.verificationId ?: "")
|
||||
|
||||
return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
|
||||
transactionId = args.verificationId ?: "",
|
||||
otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false,
|
||||
otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false,
|
||||
qrCodeText = (qrCodeVerificationTransaction as? QrCodeVerificationTransaction)?.qrCodeText,
|
||||
SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false
|
||||
)
|
||||
}
|
||||
|
|
|
@ -815,6 +815,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
room.roomId,
|
||||
action.transactionId)) {
|
||||
_requestLiveData.postValue(LiveEvent(Success(action)))
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue