Compare commits

...

3 Commits

14 changed files with 80 additions and 47 deletions

View File

@ -18,6 +18,7 @@
package me.proton.core.test.android.mocks
import kotlinx.serialization.Serializable
import io.mockk.mockk
import me.proton.core.crypto.common.pgp.Armored
import me.proton.core.crypto.common.pgp.DataPacket
@ -40,6 +41,8 @@ import me.proton.core.crypto.common.pgp.Unarmored
import me.proton.core.crypto.common.pgp.UnlockedKey
import me.proton.core.crypto.common.pgp.VerificationTime
import me.proton.core.crypto.common.pgp.VerificationContext
import me.proton.core.util.kotlin.deserialize
import me.proton.core.util.kotlin.serialize
import java.io.File
import java.util.Base64
@ -59,6 +62,15 @@ class FakePGPCrypto : PGPCrypto {
return true
}
@Serializable
data class ByteArrayList(val keys: List<ByteArray>)
override fun serializeKeys(keys: List<Unarmored>): ByteArray =
ByteArrayList(keys).serialize().encodeToByteArray()
override fun deserializeKeys(keys: ByteArray): List<Unarmored> =
keys.decodeToString().deserialize<ByteArrayList>().keys
override fun lock(unlockedKey: Unarmored, passphrase: ByteArray): Armored {
return decoder.decode(unlockedKey).decodeToString()
}

View File

@ -57,6 +57,7 @@ public final class me/proton/core/crypto/android/pgp/GOpenPGPCrypto : me/proton/
public fun decryptSessionKeyWithPassword ([B[B)Lme/proton/core/crypto/common/pgp/SessionKey;
public fun decryptText (Ljava/lang/String;[B)Ljava/lang/String;
public fun decryptTextWithPassword (Ljava/lang/String;[B)Ljava/lang/String;
public fun deserializeKeys ([B)Ljava/util/List;
public fun encryptAndSignData ([BLjava/lang/String;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public fun encryptAndSignData ([BLme/proton/core/crypto/common/pgp/SessionKey;[BLme/proton/core/crypto/common/pgp/SignatureContext;)[B
public fun encryptAndSignDataWithCompression ([BLjava/lang/String;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
@ -96,6 +97,7 @@ public final class me/proton/core/crypto/android/pgp/GOpenPGPCrypto : me/proton/
public fun isPublicKey (Ljava/lang/String;)Z
public fun isValidKey (Ljava/lang/String;)Z
public fun lock ([B[B)Ljava/lang/String;
public fun serializeKeys (Ljava/util/List;)[B
public fun signData ([B[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public fun signDataEncrypted ([B[BLjava/util/List;Lme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public fun signFile (Ljava/io/File;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;

View File

@ -1794,4 +1794,19 @@ internal class GOpenPGPCryptoTest {
val decryptedData = crypto.decryptDataWithPassword(encryptedMessage, password)
assertContentEquals(expected = data, actual = decryptedData)
}
@Test
fun serializeAndDeserializeUnlockedKey() {
// Given
val unlockedKey1 = crypto.unlock(TestKey.privateKey, TestKey.privateKeyPassphrase)
val unlockedKey2 = crypto.unlock(TestKey.privateKey2, TestKey.privateKey2Passphrase)
val keys = listOf(unlockedKey1.value, unlockedKey2.value)
// When
val serialized = crypto.serializeKeys(keys)
val deserialized = crypto.deserializeKeys(serialized)
// Then
assertTrue(deserialized.size == 2)
assertContentEquals(expected = keys[0], actual = deserialized[0])
assertContentEquals(expected = keys[1], actual = deserialized[1])
}
}

View File

@ -116,6 +116,16 @@ class GOpenPGPCrypto : PGPCrypto {
private fun newKeyRing(keys: List<CloseableUnlockedKey>) =
CloseableUnlockedKeyRing(Crypto.newKeyRing(null).apply { keys.forEach { addKey(it.value) } })
override fun serializeKeys(keys: List<Unarmored>): ByteArray =
newKeyRing(newKeys(keys)).use { it.value.serialize() }
override fun deserializeKeys(keys: ByteArray): List<Unarmored> =
CloseableUnlockedKeyRing(Crypto.newKeyRingFromBinary(keys)).use { keyRing ->
val count = keyRing.value.countEntities()
val indices = 0..<count
indices.map { index -> keyRing.value.getKey(index).serialize() }
}
// endregion
// region Public Key

View File

@ -216,6 +216,7 @@ public abstract interface class me/proton/core/crypto/common/pgp/PGPCrypto {
public abstract fun decryptSessionKeyWithPassword ([B[B)Lme/proton/core/crypto/common/pgp/SessionKey;
public abstract fun decryptText (Ljava/lang/String;[B)Ljava/lang/String;
public abstract fun decryptTextWithPassword (Ljava/lang/String;[B)Ljava/lang/String;
public abstract fun deserializeKeys ([B)Ljava/util/List;
public abstract fun encryptAndSignData ([BLjava/lang/String;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public abstract fun encryptAndSignData ([BLme/proton/core/crypto/common/pgp/SessionKey;[BLme/proton/core/crypto/common/pgp/SignatureContext;)[B
public abstract fun encryptAndSignDataWithCompression ([BLjava/lang/String;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
@ -255,6 +256,7 @@ public abstract interface class me/proton/core/crypto/common/pgp/PGPCrypto {
public abstract fun isPublicKey (Ljava/lang/String;)Z
public abstract fun isValidKey (Ljava/lang/String;)Z
public abstract fun lock ([B[B)Ljava/lang/String;
public abstract fun serializeKeys (Ljava/util/List;)[B
public abstract fun signData ([B[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public abstract fun signDataEncrypted ([B[BLjava/util/List;Lme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;
public abstract fun signFile (Ljava/io/File;[BLme/proton/core/crypto/common/pgp/SignatureContext;)Ljava/lang/String;

View File

@ -43,6 +43,20 @@ interface PGPCrypto {
*/
fun isValidKey(key: Armored): Boolean
/**
* Serialize a list of [Unarmored] unlocked keys into a [ByteArray].
*
* @see deserializeKeys
*/
fun serializeKeys(keys: List<Unarmored>): ByteArray
/**
* Deserialize a [ByteArray] of unlocked keys into an [Unarmored] list.
*
* @see serializeKeys
*/
fun deserializeKeys(keys: ByteArray): List<Unarmored>
/**
* Lock [unlockedKey] using [passphrase].
*

View File

@ -10,8 +10,8 @@
"-trimpath"
],
"build_name":"gopenpgp",
"commit_message":"feat(gopenpgp): Update to gopenpgp v2.7.5-proton.",
"upload_branch":"update-2.7.5",
"commit_message":"feat(gopenpgp): Update to gopenpgp v2.8.0-alpha.1-proton.",
"upload_branch":"update-2.8.0-alpha.1",
"java_pkg":"com.proton.gopenpgp",
"targets":[
"android"
@ -20,7 +20,7 @@
{
"module":{
"path":"github.com/ProtonMail/gopenpgp/v2",
"version":"v2.7.5-proton"
"version":"v2.8.0-alpha.1-proton"
},
"packages":[
"crypto",

Binary file not shown.

Binary file not shown.

View File

@ -20,6 +20,7 @@ import studio.forface.easygradle.dsl.*
plugins {
protonKotlinLibrary
kotlin("plugin.serialization")
}
protonBuild {

View File

@ -19,6 +19,7 @@
package me.proton.core.key.domain
import io.mockk.mockk
import kotlinx.serialization.Serializable
import me.proton.core.crypto.common.context.CryptoContext
import me.proton.core.crypto.common.keystore.EncryptedByteArray
import me.proton.core.crypto.common.keystore.EncryptedString
@ -51,6 +52,8 @@ import me.proton.core.crypto.common.pgp.VerificationTime
import me.proton.core.crypto.common.srp.Auth
import me.proton.core.crypto.common.srp.SrpCrypto
import me.proton.core.crypto.common.srp.SrpProofs
import me.proton.core.util.kotlin.deserialize
import me.proton.core.util.kotlin.serialize
import java.io.File
open class TestCryptoContext : CryptoContext {
@ -572,11 +575,19 @@ open class TestCryptoContext : CryptoContext {
override suspend fun getCurrentTime(): Long = 0
override fun isPublicKey(key: Armored): Boolean = key.contains("privateKey")
override fun isPrivateKey(key: Armored): Boolean =
key.contains("privateKey")
override fun isPrivateKey(key: Armored): Boolean = key.contains("privateKey")
override fun isValidKey(key: Armored): Boolean = key.contains("privateKey")
override fun serializeKeys(keys: List<Unarmored>): ByteArray =
ByteArrayList(keys).serialize().encodeToByteArray()
override fun deserializeKeys(keys: ByteArray): List<Unarmored> =
keys.decodeToString().deserialize<ByteArrayList>().keys
}
@Serializable
data class ByteArrayList(val keys: List<ByteArray>)
override val pgpCrypto = TestPGPCrypto()
override val srpCrypto = object : SrpCrypto {

View File

@ -10,33 +10,6 @@ public final class me/proton/core/userrecovery/domain/LogTag {
public static final field INSTANCE Lme/proton/core/userrecovery/domain/LogTag;
}
public final class me/proton/core/userrecovery/domain/usecase/ByteArrayList {
public static final field Companion Lme/proton/core/userrecovery/domain/usecase/ByteArrayList$Companion;
public fun <init> (Ljava/util/List;)V
public final fun component1 ()Ljava/util/List;
public final fun copy (Ljava/util/List;)Lme/proton/core/userrecovery/domain/usecase/ByteArrayList;
public static synthetic fun copy$default (Lme/proton/core/userrecovery/domain/usecase/ByteArrayList;Ljava/util/List;ILjava/lang/Object;)Lme/proton/core/userrecovery/domain/usecase/ByteArrayList;
public fun equals (Ljava/lang/Object;)Z
public final fun getKeys ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class me/proton/core/userrecovery/domain/usecase/ByteArrayList$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lme/proton/core/userrecovery/domain/usecase/ByteArrayList$$serializer;
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lme/proton/core/userrecovery/domain/usecase/ByteArrayList;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lme/proton/core/userrecovery/domain/usecase/ByteArrayList;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class me/proton/core/userrecovery/domain/usecase/ByteArrayList$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class me/proton/core/userrecovery/domain/usecase/GetRecoveryFile {
public fun <init> (Lme/proton/core/user/domain/UserManager;Lme/proton/core/crypto/common/context/CryptoContext;)V
public final fun invoke (Lme/proton/core/domain/entity/UserId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

View File

@ -18,13 +18,12 @@
package me.proton.core.userrecovery.domain.usecase
import kotlinx.serialization.Serializable
import me.proton.core.crypto.common.context.CryptoContext
import me.proton.core.crypto.common.keystore.use
import me.proton.core.crypto.common.pgp.EncryptedMessage
import me.proton.core.domain.entity.UserId
import me.proton.core.key.domain.unlockOrNull
import me.proton.core.user.domain.UserManager
import me.proton.core.util.kotlin.serialize
import javax.inject.Inject
/**
@ -46,15 +45,10 @@ class GetRecoveryFile @Inject constructor(
val privateKeys = activeKeys.map { it.privateKey }
val unlockedKeys = privateKeys.mapNotNull { it.unlockOrNull(cryptoContext)?.unlockedKey }
check(unlockedKeys.isNotEmpty())
val byteArrayList = ByteArrayList(unlockedKeys.map { it.value })
val fileByteArray = byteArrayList.serialize().encodeToByteArray()
val secret = pgpCrypto.getBase64Decoded(primaryKeyRecoverySecret)
return pgpCrypto.encryptDataWithPassword(fileByteArray, secret)
pgpCrypto.serializeKeys(unlockedKeys.map { it.value }).use {
unlockedKeys.forEach { unlockedKey -> unlockedKey.close() }
val secret = pgpCrypto.getBase64Decoded(primaryKeyRecoverySecret)
return pgpCrypto.encryptDataWithPassword(it.array, secret)
}
}
}
@Deprecated("Replace with proper binary serialization (pgp).")
@Serializable
data class ByteArrayList(
val keys: List<ByteArray>
)

View File

@ -29,7 +29,6 @@ import me.proton.core.key.domain.entity.key.UnlockedPrivateKey
import me.proton.core.key.domain.lock
import me.proton.core.user.domain.UserManager
import me.proton.core.user.domain.repository.PassphraseRepository
import me.proton.core.util.kotlin.deserialize
import javax.inject.Inject
class GetRecoveryPrivateKeys @Inject constructor(
@ -48,9 +47,9 @@ class GetRecoveryPrivateKeys @Inject constructor(
secrets.forEach { secret ->
val decodedSecret = pgpCrypto.getBase64Decoded(secret)
pgpCrypto.decryptDataWithPasswordOrNull(message, decodedSecret)?.let {
val byteArrayList = it.decodeToString().deserialize<ByteArrayList>()
val unlockedKeys = pgpCrypto.deserializeKeys(it)
val passphrase = checkNotNull(passphraseRepository.getPassphrase(userId))
return byteArrayList.keys.map { key ->
return unlockedKeys.map { key ->
UnlockedPrivateKey(TempUnlockedKey(key), isPrimary = false).use { unlocked ->
unlocked.lock(cryptoContext, passphrase = passphrase, isPrimary = false)
}