Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Commit

Permalink
Merge branch 'release/v0.9.35'
Browse files Browse the repository at this point in the history
  • Loading branch information
bmarty committed May 20, 2020
2 parents 353034e + 7e546cd commit 30b243a
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 39 deletions.
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
Changes to Matrix Android SDK in 0.9.35 (2020-05-20)
=======================================================

Bugfix:
- Fix crash on RoomAccountData, on some devices

Others:
- support new key agreement method for SAS (riotX/#1374)

Changes to Matrix Android SDK in 0.9.34 (2020-05-13)
=======================================================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,28 +184,47 @@ class IncomingSASVerificationTransaction(transactionId: String, otherUserID: Str
// using the result as the shared secret.

getSAS().setTheirPublicKey(otherKey)
//(Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
// - the Matrix ID of the user who sent the m.key.verification.start message,
// - the device ID of the device that sent the m.key.verification.start message,
// - the Matrix ID of the user who sent the m.key.verification.accept message,
// - he device ID of the device that sent the m.key.verification.accept message
// - the transaction ID.
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS" +
"$otherUserId$otherDeviceId" +
"${session.myUserId}${session.requireCrypto().myDevice.deviceId}" +
transactionId
//decimal: generate five bytes by using HKDF.
//emoji: generate six bytes by using HKDF.
shortCodeBytes = getSAS().generateShortCode(sasInfo, 6)
shortCodeBytes = calculateSASBytes(session)

Log.e(LOG_TAG, "************ BOB CODE ${getDecimalCodeRepresentation(shortCodeBytes!!)}")
Log.e(LOG_TAG, "************ BOB EMOJI CODE ${getShortCodeRepresentation(KeyVerificationStart.SAS_MODE_EMOJI)}")

state = SASVerificationTxState.ShortCodeReady
}

private fun calculateSASBytes(session: CryptoSession): ByteArray {
val myDeviceId = session.requireCrypto().myDevice.deviceId
when (accepted?.keyAgreementProtocol) {
KEY_AGREEMENT_V1 -> {
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
// - the Matrix ID of the user who sent the m.key.verification.start message,
// - the device ID of the device that sent the m.key.verification.start message,
// - the Matrix ID of the user who sent the m.key.verification.accept message,
// - he device ID of the device that sent the m.key.verification.accept message
// - the transaction ID.
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$otherUserId$otherDeviceId${session.myUserId}$myDeviceId$transactionId"

// decimal: generate five bytes by using HKDF.
// emoji: generate six bytes by using HKDF.
return getSAS().generateShortCode(sasInfo, 6)
}
KEY_AGREEMENT_V2 -> {
// Adds the SAS public key, and separate by |
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|" +
"$otherUserId|$otherDeviceId|$otherKey|" +
"${session.myUserId}|$myDeviceId|${getSAS().publicKey}|" +
transactionId
return getSAS().generateShortCode(sasInfo, 6)
}
else -> {
// Protocol has been checked earlier
throw IllegalArgumentException()
}
}
}

override fun onKeyVerificationMac(session: CryptoSession, vKey: KeyVerificationMac) {
Log.d(LOG_TAG, "## SAS received mac for request id:$transactionId")
//Check for state?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,28 +156,46 @@ class OutgoingSASVerificationRequest(transactionId: String, otherUserId: String,

if (accepted!!.commitment.equals(otherCommitment)) {
getSAS().setTheirPublicKey(otherKey)
//(Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
// - the Matrix ID of the user who sent the m.key.verification.start message,
// - the device ID of the device that sent the m.key.verification.start message,
// - the Matrix ID of the user who sent the m.key.verification.accept message,
// - he device ID of the device that sent the m.key.verification.accept message
// - the transaction ID.
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS" +
"${session.myUserId}${session.requireCrypto().myDevice.deviceId}" +
"$otherUserId$otherDeviceId" +
transactionId
//decimal: generate five bytes by using HKDF.
//emoji: generate six bytes by using HKDF.
shortCodeBytes = getSAS().generateShortCode(sasInfo, 6)
shortCodeBytes = calculateSASBytes(session)
state = SASVerificationTxState.ShortCodeReady
} else {
//bad commitement
cancel(session, CancelCode.MismatchedCommitment)
}
}


private fun calculateSASBytes(session: CryptoSession): ByteArray {
val userId = session.myUserId
val deviceId = session.requireCrypto().myDevice.deviceId
when (accepted?.keyAgreementProtocol) {
KEY_AGREEMENT_V1 -> {
// (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function,
// the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of:
// - the string “MATRIX_KEY_VERIFICATION_SAS”,
// - the Matrix ID of the user who sent the m.key.verification.start message,
// - the device ID of the device that sent the m.key.verification.start message,
// - the Matrix ID of the user who sent the m.key.verification.accept message,
// - he device ID of the device that sent the m.key.verification.accept message
// - the transaction ID.
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS$userId$deviceId$otherUserId$otherDeviceId$transactionId"

// decimal: generate five bytes by using HKDF.
// emoji: generate six bytes by using HKDF.
return getSAS().generateShortCode(sasInfo, 6)
}
KEY_AGREEMENT_V2 -> {
// Adds the SAS public key, and separate by |
val sasInfo = "MATRIX_KEY_VERIFICATION_SAS|$userId|$deviceId|${getSAS().publicKey}|$otherUserId|$otherDeviceId|$otherKey|$transactionId"
return getSAS().generateShortCode(sasInfo, 6)
}
else -> {
// Protocol has been checked earlier
throw IllegalArgumentException()
}
}
}

override fun onKeyVerificationMac(session: CryptoSession, vKey: KeyVerificationMac) {
Log.d(LOG_TAG, "## onKeyVerificationMac id:$transactionId")
if (state != SASVerificationTxState.OnKeyReceived
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ abstract class SASVerificationTransaction(transactionId: String,
const val SAS_MAC_SHA256_LONGKDF = "hmac-sha256"
const val SAS_MAC_SHA256 = "hkdf-hmac-sha256"


// Deprecated maybe removed later, use V2
const val KEY_AGREEMENT_V1 = "curve25519"
const val KEY_AGREEMENT_V2 = "curve25519-hkdf-sha256"

//ordered by preferred order
val KNOWN_AGREEMENT_PROTOCOLS = listOf(MXKey.KEY_CURVE_25519_TYPE)
val KNOWN_AGREEMENT_PROTOCOLS = listOf(KEY_AGREEMENT_V2, KEY_AGREEMENT_V1)
//ordered by preferred order
val KNOWN_HASHES = listOf("sha256")
//ordered by preferred order
Expand Down
4 changes: 2 additions & 2 deletions matrix-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 934
versionName "0.9.34"
versionCode 935
versionName "0.9.35"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

// Enable multi dex for test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,63 @@ class SASTest {

}

@Test
fun test_key_agreement_protocols_v1() {
val context = InstrumentationRegistry.getContext()

val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()

val bobSession = cryptoTestData.secondSession

val protocols = listOf(SASVerificationTransaction.KEY_AGREEMENT_V1)
val tid = "00000000"

//Bob should receive a cancel
var acceptToDeviceEvent: Event? = null
val cancelLatch = CountDownLatch(1)
bobSession!!.dataHandler.addListener(object : MXEventListener() {
override fun onToDeviceEvent(event: Event?) {
if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_ACCEPT) {
if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) {
acceptToDeviceEvent = event
cancelLatch.countDown()
}
}
}
})

val aliceSession = cryptoTestData.firstSession
val aliceUserID = aliceSession.myUserId
val aliceDevice = aliceSession.crypto!!.myDevice.deviceId

val aliceListener = object : VerificationManager.VerificationManagerListener {
override fun transactionCreated(tx: VerificationTransaction) {}

override fun transactionUpdated(tx: VerificationTransaction) {
if ((tx as IncomingSASVerificationTransaction).uxState === IncomingSASVerificationTransaction.State.SHOW_ACCEPT) {
(tx as IncomingSASVerificationTransaction).performAccept(bobSession)
}
}

override fun markedAsManuallyVerified(userId: String, deviceId: String) {}
}
aliceSession.crypto?.shortCodeVerificationManager?.addListener(aliceListener)


fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)

mTestHelper.await(cancelLatch)


val acceptReq = JsonUtils.getBasicGson()
.fromJson(acceptToDeviceEvent!!.content, KeyVerificationAccept::class.java)
assertEquals("Request should be accepted with v1 protocol", SASVerificationTransaction.KEY_AGREEMENT_V1, acceptReq.keyAgreementProtocol)

cryptoTestData.clear(context)

}


@Test
fun test_key_agreement_macs_Must_include_hmac_sha256() {
val context = InstrumentationRegistry.getContext()
Expand Down Expand Up @@ -415,6 +472,7 @@ class SASTest {
//check that agreement is valid
assertTrue("Agreed Protocol should be Valid", accepted!!.isValid())
assertTrue("Agreed Protocol should be known by alice", startReq!!.keyAgreementProtocols!!.contains(accepted!!.keyAgreementProtocol))
assertEquals("Agreed Protocol should be V2", "curve25519-hkdf-sha256", accepted!!.keyAgreementProtocol)
assertTrue("Hash should be known by alice", startReq!!.hashes!!.contains(accepted!!.hash))
assertTrue("Hash should be known by alice", startReq!!.messageAuthenticationCodes!!.contains(accepted!!.messageAuthenticationCode))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@

package org.matrix.androidsdk.data;

import com.google.gson.JsonObject;

import androidx.annotation.Nullable;

import com.google.gson.JsonObject;

import org.matrix.androidsdk.core.JsonUtils;
import org.matrix.androidsdk.rest.model.Event;
import org.matrix.androidsdk.rest.model.TaggedEventInfo;
Expand Down Expand Up @@ -80,7 +80,12 @@ public void handleEvent(Event event) {
}

// Store by default the content of all the provided events.
eventContentsMap.put(eventType, jsonObject);
if (jsonObject != null) {
eventContentsMap.put(eventType, jsonObject);
} else {
// Store an empty JsonObject
eventContentsMap.put(eventType, new JsonObject());
}
}

/**
Expand Down Expand Up @@ -177,10 +182,10 @@ public TaggedEventInfo hiddenEventInfo(String eventId) {

/**
* Provide the content of an handled event according to its type.
* @apiNote Use this method only when no dedicated method exists for the requested event type.
*
* @param eventType the type of the requested event.
* @return the event content casted as JsonObject (null if no event has been handled with this type).
* @apiNote Use this method only when no dedicated method exists for the requested event type.
*/
@Nullable
public JsonObject eventContent(String eventType) {
Expand All @@ -189,16 +194,16 @@ public JsonObject eventContent(String eventType) {


/**
* @deprecated use hasRoomTags() instead.
* @return true if some tags have been defined for the room
* @deprecated use hasRoomTags() instead.
*/
public boolean hasTags() {
return hasRoomTags();
}

/**
* @deprecated use getRoomTagsKeys() instead.
* @return the list of the keys used to defined the room tags, or null if there is no tag
* @deprecated use getRoomTagsKeys() instead.
*/
@Nullable
public Set<String> getKeys() {
Expand Down

0 comments on commit 30b243a

Please sign in to comment.