Skip to content

Commit

Permalink
Merge pull request #440 from WalletConnect/develop
Browse files Browse the repository at this point in the history
Develop to Master
  • Loading branch information
jakubuid authored Oct 28, 2022
2 parents 2021f25 + 071fea9 commit eaedba8
Show file tree
Hide file tree
Showing 131 changed files with 1,169 additions and 3,499 deletions.
3 changes: 2 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ WalletConnect v2 protocols for Android applications.
* [Core SDK](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/develop/androidCore)
* [Sign SDK](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/develop/sign)
* [Auth SDK](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/develop/auth)
* [Chat SDK](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/develop/chat)

## License
WalletConnect v2 is released under the Apache 2.0 license. [See LICENSE](https://github.com/WalletConnect/WalletConnectKotlinV2/blob/feature/develop/LICENSE) for details.
WalletConnect v2 is released under the Apache 2.0 license. [See LICENSE](https://github.com/WalletConnect/WalletConnectKotlinV2/blob/feature/develop/LICENSE) for details.
4 changes: 2 additions & 2 deletions androidCore/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {

project.apply {
extra[KEY_PUBLISH_ARTIFACT_ID] = "android-core-impl"
extra[KEY_PUBLISH_VERSION] = "1.1.0"
extra[KEY_PUBLISH_VERSION] = "1.2.0"
extra[KEY_SDK_NAME] = "Android Core Impl"
}

Expand Down Expand Up @@ -51,7 +51,7 @@ sqldelight {

dependencies {
debugApi(project(":androidCore:sdk"))
releaseApi("com.walletconnect:android-core:1.1.0")
releaseApi("com.walletconnect:android-core:1.2.0")

bouncyCastle()
coroutines()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ import com.walletconnect.android.internal.common.exception.WalletConnectExceptio

class UnknownEnvelopeTypeException(override val message: String?) : WalletConnectException(message)
class MissingParticipantsException(override val message: String?) : WalletConnectException(message)
class MissingReceiverPublicKeyException(override val message: String?) : WalletConnectException(message)
class MissingKeyException(override val message: String?) : WalletConnectException(message)
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
package com.walletconnect.android.impl.data.codec

import com.walletconnect.android.impl.common.MissingParticipantsException
import com.walletconnect.android.impl.common.MissingReceiverPublicKeyException
import com.walletconnect.android.impl.common.MissingKeyException
import com.walletconnect.android.impl.common.UnknownEnvelopeTypeException
import com.walletconnect.android.internal.common.model.Participants
import com.walletconnect.android.internal.common.model.SymmetricKey
import com.walletconnect.android.internal.common.model.EnvelopeType
import com.walletconnect.android.impl.crypto.Codec
import com.walletconnect.android.impl.utils.SELF_PARTICIPANT_CONTEXT
import com.walletconnect.android.internal.common.crypto.KeyManagementRepository
import com.walletconnect.foundation.common.model.PublicKey
import com.walletconnect.foundation.common.model.Topic
Expand Down Expand Up @@ -47,14 +48,17 @@ internal class ChaChaPolyCodec(private val keyManagementRepository: KeyManagemen

@Throws(
UnknownEnvelopeTypeException::class,
MissingReceiverPublicKeyException::class
MissingKeyException::class
)
override fun decrypt(topic: Topic, cipherText: String): String {
val encryptedPayloadBytes = Base64.decode(cipherText)

return when (val envelopeType = encryptedPayloadBytes.envelopeType) {
EnvelopeType.ZERO.id -> decryptType0(topic, encryptedPayloadBytes)
EnvelopeType.ONE.id -> decryptType1(encryptedPayloadBytes, keyManagementRepository.getSelfParticipant(topic))
EnvelopeType.ONE.id -> decryptType1(
encryptedPayloadBytes,
keyManagementRepository.getPublicKey("$SELF_PARTICIPANT_CONTEXT${topic.value}")
)
else -> throw UnknownEnvelopeTypeException("Decrypt; Unknown envelope type: $envelopeType")
}
}
Expand All @@ -70,14 +74,14 @@ internal class ChaChaPolyCodec(private val keyManagementRepository: KeyManagemen
byteBuffer.get(nonce)
byteBuffer.get(encryptedMessageBytes)

val symmetricKey = keyManagementRepository.getSymmetricKey(topic)
val symmetricKey = keyManagementRepository.getSymmetricKey(topic.value)
val decryptedTextBytes = decryptPayload(symmetricKey, nonce, encryptedMessageBytes)

return String(decryptedTextBytes, Charsets.UTF_8)
}

private fun decryptType1(encryptedPayloadBytes: ByteArray, receiverPublicKey: PublicKey?): String {
if (receiverPublicKey == null) throw MissingReceiverPublicKeyException("Missing receiver public key")
if (receiverPublicKey == null) throw MissingKeyException("Missing receiver public key")

val envelopeType = ByteArray(ENVELOPE_TYPE_SIZE)
val nonce = ByteArray(NONCE_SIZE)
Expand All @@ -99,7 +103,7 @@ internal class ChaChaPolyCodec(private val keyManagementRepository: KeyManagemen
}

private fun encryptEnvelopeType0(topic: Topic, nonceBytes: ByteArray, input: ByteArray, envelopeType: EnvelopeType): String {
val symmetricKey = keyManagementRepository.getSymmetricKey(topic)
val symmetricKey = keyManagementRepository.getSymmetricKey(topic.value)
val cipherBytes = encryptPayload(symmetricKey, nonceBytes, input)
val payloadSize = cipherBytes.size + NONCE_SIZE + ENVELOPE_TYPE_SIZE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,65 @@

package com.walletconnect.android.impl.data.repository

import com.walletconnect.android.internal.common.storage.KeyStore
import com.walletconnect.android.internal.common.model.SymmetricKey
import com.walletconnect.android.impl.common.MissingKeyException
import com.walletconnect.android.internal.common.crypto.KeyManagementRepository
import com.walletconnect.android.internal.common.model.SymmetricKey
import com.walletconnect.android.internal.common.storage.KeyStore
import com.walletconnect.foundation.common.model.Key
import com.walletconnect.foundation.common.model.PrivateKey
import com.walletconnect.foundation.common.model.PublicKey
import com.walletconnect.foundation.common.model.Topic
import com.walletconnect.util.bytesToHex
import com.walletconnect.util.hexToBytes
import com.walletconnect.utils.Empty
import org.bouncycastle.crypto.digests.SHA256Digest
import org.bouncycastle.crypto.generators.HKDFBytesGenerator
import org.bouncycastle.crypto.params.HKDFParameters
import org.bouncycastle.math.ec.rfc7748.X25519
import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.KeyGenerator
import com.walletconnect.foundation.common.model.Key as WCKey

//todo: Refactor
internal class BouncyCastleKeyManagementRepository(private val keyChain: KeyStore) : KeyManagementRepository {
override fun generateAndStoreSymmetricKey(topic: Topic): SymmetricKey {
val symmetricKey = generateSymmetricKey()
keyChain.setKey(topic.value, symmetricKey)
return symmetricKey
override fun setKey(key: Key, tag: String) {
keyChain.setKey(tag, key)
}

override fun generateSymmetricKey(): SymmetricKey = SymmetricKey(createSymmetricKey().bytesToHex())
override fun getPublicKey(tag: String): PublicKey {
val key = keyChain.getKey(tag) ?: throw MissingKeyException("No SymmetricKey for tag: $tag")
return PublicKey(key)
}

override fun setSymmetricKey(topic: Topic, symmetricKey: SymmetricKey) {
keyChain.setKey(topic.value, symmetricKey)
override fun getSymmetricKey(tag: String): SymmetricKey {
val key = keyChain.getKey(tag) ?: throw MissingKeyException("No PublicKey for tag: $tag")
return SymmetricKey(key)
}

override fun getSymmetricKey(topic: Topic): SymmetricKey {
val symmetricKey = keyChain.getKey(topic.value)
override fun getKeyAgreement(topic: Topic): Pair<PublicKey, PublicKey> {
val tag = "$KEY_AGREEMENT_CONTEXT${topic.value}"
val (selfPublic, peerPublic) = keyChain.getKeys(tag)

return Pair(PublicKey(selfPublic), PublicKey(peerPublic))
}

return SymmetricKey(symmetricKey)
override fun setKeyAgreement(topic: Topic, self: PublicKey, peer: PublicKey) {
val tag = "$KEY_AGREEMENT_CONTEXT${topic.value}"
keyChain.setKeys(tag, self, peer)
}

override fun generateKeyPair(): PublicKey {
val publicKey = ByteArray(KEY_SIZE)
val privateKey = ByteArray(KEY_SIZE)
X25519.generatePrivateKey(SecureRandom(ByteArray(KEY_SIZE)), privateKey)
X25519.generatePublicKey(privateKey, 0, publicKey, 0)
setKeyPair(PublicKey(publicKey.bytesToHex().lowercase()), PrivateKey(privateKey.bytesToHex().lowercase()))

setKeyPair(PublicKey(publicKey.bytesToHex().lowercase()), PrivateKey(privateKey.bytesToHex().lowercase()))
return PublicKey(publicKey.bytesToHex().lowercase())
}

override fun generateTopicFromKeyAgreement(self: PublicKey, peer: PublicKey): Topic {
val symmetricKey = generateSymmetricKeyFromKeyAgreement(self, peer)
val topic = Topic(sha256(symmetricKey.keyAsHex))
keyChain.setKey(topic.value.lowercase(), symmetricKey)
setKeyAgreement(topic, self, peer)

return topic
}

override fun getTopicFromKey(key: WCKey): Topic = Topic(sha256(key.keyAsHex))

override fun setSelfParticipant(key: PublicKey, topic: Topic) {
val tag = "$SELF_PARTICIPANT_CONTEXT${topic.value}"
keyChain.setKey(tag, key)
}

override fun getSelfParticipant(topic: Topic): PublicKey? {
val keyAsHex = keyChain.getKey("$SELF_PARTICIPANT_CONTEXT${topic.value}")
return if (keyAsHex == String.Empty) null else PublicKey(keyAsHex)
override fun generateAndStoreSymmetricKey(topic: Topic): SymmetricKey {
val symmetricKey = SymmetricKey(createSymmetricKey().bytesToHex())
keyChain.setKey(topic.value, symmetricKey)
return symmetricKey
}

override fun generateSymmetricKeyFromKeyAgreement(self: PublicKey, peer: PublicKey): SymmetricKey {
Expand All @@ -81,6 +73,16 @@ internal class BouncyCastleKeyManagementRepository(private val keyChain: KeyStor
return SymmetricKey(symmetricKeyBytes.bytesToHex())
}

override fun generateTopicFromKeyAgreement(self: PublicKey, peer: PublicKey): Topic {
val symmetricKey = generateSymmetricKeyFromKeyAgreement(self, peer)
val topic = Topic(sha256(symmetricKey.keyAsHex))
keyChain.setKey(topic.value.lowercase(), symmetricKey)
setKeyAgreement(topic, self, peer)
return topic
}

override fun getTopicFromKey(key: Key): Topic = Topic(sha256(key.keyAsHex))

override fun removeKeys(tag: String) {
val (publicKey, _) = keyChain.getKeys(tag)
with(keyChain) {
Expand All @@ -89,23 +91,11 @@ internal class BouncyCastleKeyManagementRepository(private val keyChain: KeyStor
}
}

override fun getKeyAgreement(topic: Topic): Pair<PublicKey, PublicKey> {
val tag = "$KEY_AGREEMENT_CONTEXT${topic.value}"
val (selfPublic, peerPublic) = keyChain.getKeys(tag)

return Pair(PublicKey(selfPublic), PublicKey(peerPublic))
}

private fun setKeyAgreement(topic: Topic, self: PublicKey, peer: PublicKey) {
val tag = "$KEY_AGREEMENT_CONTEXT${topic.value}"
keyChain.setKeys(tag, self, peer)
}

internal fun setKeyPair(publicKey: PublicKey, privateKey: PrivateKey) {
keyChain.setKeys(publicKey.keyAsHex, publicKey, privateKey)
}

internal fun getKeyPair(wcKey: WCKey): Pair<PublicKey, PrivateKey> {
internal fun getKeyPair(wcKey: Key): Pair<PublicKey, PrivateKey> {
val (publicKeyHex, privateKeyHex) = keyChain.getKeys(wcKey.keyAsHex)

return Pair(PublicKey(publicKeyHex), PrivateKey(privateKeyHex))
Expand Down Expand Up @@ -145,6 +135,5 @@ internal class BouncyCastleKeyManagementRepository(private val keyChain: KeyStor
const val AES: String = "AES"

const val KEY_AGREEMENT_CONTEXT = "key_agreement/"
const val SELF_PARTICIPANT_CONTEXT = "self_participant/"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ internal class JsonRpcInteractor(
private val chaChaPolyCodec: Codec,
private val jsonRpcHistory: JsonRpcHistory,
) : JsonRpcInteractorInterface {
private val serializer: JsonRpcSerializer
get() = wcKoinApp.koin.get()
private val serializer: JsonRpcSerializer get() = wcKoinApp.koin.get()

private val _clientSyncJsonRpc: MutableSharedFlow<WCRequest> = MutableSharedFlow()
override val clientSyncJsonRpc: SharedFlow<WCRequest> = _clientSyncJsonRpc.asSharedFlow()
Expand Down Expand Up @@ -57,7 +56,7 @@ internal class JsonRpcInteractor(
}
}

override fun publishJsonRpcRequests(
override fun publishJsonRpcRequest(
topic: Topic,
params: IrnParams,
payload: JsonRpcClientSync<*>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.walletconnect.android.impl.utils

const val SELF_PARTICIPANT_CONTEXT = "self_participant/"
const val SELF_INVITE_PUBLIC_KEY_CONTEXT = "self_inviteKey/"
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

package com.walletconnect.utils

import com.walletconnect.android.impl.di.AndroidCoreDITags
import com.walletconnect.android.impl.utils.CURRENT_TIME_IN_SECONDS
import com.walletconnect.android.impl.utils.Logger
import com.walletconnect.android.internal.common.SerializableJsonRpc
import com.walletconnect.android.internal.common.model.Expiry
import org.koin.core.module.KoinDefinition
Expand Down Expand Up @@ -32,12 +30,7 @@ val String.Companion.HexPrefix
get() = "0x"

inline fun <reified T : SerializableJsonRpc> Module.addSerializerEntry(value: KClass<T>): KoinDefinition<*> =
single(qualifier = named("key_${T::class.getFullName()}")) {
value
}

single(qualifier = named("key_${T::class.getFullName()}")) { value }

fun Module.addDeserializerEntry(key: String, value: KClass<*>): KoinDefinition<*> =
single(qualifier = named("${key::class.getFullName()}_${value::class.getFullName()}_$key")) {
key to value
}
single(qualifier = named("${key::class.getFullName()}_${value::class.getFullName()}_$key")) { key to value }
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ internal class BouncyCastleCryptoRepositoryTest {
val symKey = sut.generateAndStoreSymmetricKey(topicVO)
assert(symKey.keyAsHex.length == 64)

val secretKey = sut.getSymmetricKey(topicVO)
val secretKey = sut.getSymmetricKey(topicVO.value)
assertEquals(symKey.keyAsHex, secretKey.keyAsHex)
assert(secretKey.keyAsHex.length == 64)
}
Expand Down Expand Up @@ -99,12 +99,12 @@ internal class BouncyCastleCryptoRepositoryTest {
fun `Generated SymmetricKey gets removed when using a TopicVO as the tag for removeKeys`() {
val symKey = sut.generateAndStoreSymmetricKey(topicVO)

val secretKey = sut.getSymmetricKey(topicVO)
val secretKey = sut.getSymmetricKey(topicVO.value)
assertEquals(symKey.keyAsHex, secretKey.keyAsHex)

sut.removeKeys(topicVO.value)

val secretKeyAfterRemoval = sut.getSymmetricKey(topicVO)
val secretKeyAfterRemoval = sut.getSymmetricKey(topicVO.value)
assertEquals(String.Empty, secretKeyAfterRemoval.keyAsHex)
}
}
Loading

0 comments on commit eaedba8

Please sign in to comment.