Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import android.os.AsyncTask
import androidx.lifecycle.MutableLiveData
import com.flowcrypt.email.api.retrofit.LoadingState
import com.flowcrypt.email.api.retrofit.request.node.DecryptKeyRequest
import com.flowcrypt.email.api.retrofit.request.node.GenerateKeyRequest
import com.flowcrypt.email.api.retrofit.request.node.NodeRequest
import com.flowcrypt.email.api.retrofit.request.node.NodeRequestWrapper
import com.flowcrypt.email.api.retrofit.request.node.ParseDecryptMsgRequest
Expand All @@ -19,12 +18,10 @@ import com.flowcrypt.email.api.retrofit.request.node.ZxcvbnStrengthBarRequest
import com.flowcrypt.email.api.retrofit.response.base.Result
import com.flowcrypt.email.api.retrofit.response.node.BaseNodeResponse
import com.flowcrypt.email.api.retrofit.response.node.DecryptKeyResult
import com.flowcrypt.email.api.retrofit.response.node.GenerateKeyResult
import com.flowcrypt.email.api.retrofit.response.node.NodeResponseWrapper
import com.flowcrypt.email.api.retrofit.response.node.ParseDecryptedMsgResult
import com.flowcrypt.email.api.retrofit.response.node.ParseKeysResult
import com.flowcrypt.email.api.retrofit.response.node.ZxcvbnStrengthBarResult
import com.flowcrypt.email.model.PgpContact
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

Expand Down Expand Up @@ -64,12 +61,6 @@ class NodeRepository : PgpApiRepository {
load(requestCode, liveData, request)
}

override suspend fun createPrivateKey(context: Context, passphrase: String, pgpContacts: List<PgpContact>): Result<GenerateKeyResult?> =
withContext(Dispatchers.IO) {
val apiService = NodeRetrofitHelper.getRetrofit()!!.create(NodeService::class.java)
getResult(call = { apiService.generateKeySuspend(GenerateKeyRequest(passphrase, pgpContacts)) })
}

override suspend fun zxcvbnStrengthBar(context: Context, guesses: Double): Result<ZxcvbnStrengthBarResult?> =
withContext(Dispatchers.IO) {
val apiService = NodeRetrofitHelper.getRetrofit()!!.create(NodeService::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import com.flowcrypt.email.api.retrofit.request.node.DecryptKeyRequest
import com.flowcrypt.email.api.retrofit.request.node.EncryptFileRequest
import com.flowcrypt.email.api.retrofit.request.node.EncryptKeyRequest
import com.flowcrypt.email.api.retrofit.request.node.EncryptMsgRequest
import com.flowcrypt.email.api.retrofit.request.node.GenerateKeyRequest
import com.flowcrypt.email.api.retrofit.request.node.GmailBackupSearchRequest
import com.flowcrypt.email.api.retrofit.request.node.ParseDecryptMsgRequest
import com.flowcrypt.email.api.retrofit.request.node.ParseKeysRequest
Expand All @@ -23,7 +22,6 @@ import com.flowcrypt.email.api.retrofit.response.node.DecryptedFileResult
import com.flowcrypt.email.api.retrofit.response.node.EncryptKeyResult
import com.flowcrypt.email.api.retrofit.response.node.EncryptedFileResult
import com.flowcrypt.email.api.retrofit.response.node.EncryptedMsgResult
import com.flowcrypt.email.api.retrofit.response.node.GenerateKeyResult
import com.flowcrypt.email.api.retrofit.response.node.GmailBackupSearchResult
import com.flowcrypt.email.api.retrofit.response.node.ParseDecryptedMsgResult
import com.flowcrypt.email.api.retrofit.response.node.ParseKeysResult
Expand Down Expand Up @@ -87,12 +85,6 @@ interface NodeService {
@Streaming
fun decryptFile(@Body request: DecryptFileRequest): Call<DecryptedFileResult>

@POST("/")
fun generateKey(@Body request: GenerateKeyRequest): Call<GenerateKeyResult>

@POST("/")
suspend fun generateKeySuspend(@Body request: GenerateKeyRequest): Response<GenerateKeyResult>

@POST("/")
fun zxcvbnStrengthBar(@Body request: ZxcvbnStrengthBarRequest): Call<ZxcvbnStrengthBarResult>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ import com.flowcrypt.email.api.retrofit.request.node.ZxcvbnStrengthBarRequest
import com.flowcrypt.email.api.retrofit.response.base.Result
import com.flowcrypt.email.api.retrofit.response.model.node.NodeKeyDetails
import com.flowcrypt.email.api.retrofit.response.node.DecryptKeyResult
import com.flowcrypt.email.api.retrofit.response.node.GenerateKeyResult
import com.flowcrypt.email.api.retrofit.response.node.NodeResponseWrapper
import com.flowcrypt.email.api.retrofit.response.node.ParseDecryptedMsgResult
import com.flowcrypt.email.api.retrofit.response.node.ParseKeysResult
import com.flowcrypt.email.api.retrofit.response.node.ZxcvbnStrengthBarResult
import com.flowcrypt.email.model.PgpContact

/**
* It's an entry point of all requests to work with PGP actions.
Expand Down Expand Up @@ -68,16 +66,6 @@ interface PgpApiRepository : BaseApiRepository {

suspend fun decryptKey(context: Context, armoredKey: String, passphrases: List<String>): Result<DecryptKeyResult>

/**
* Generate a private key using the given parameters.
*
* @param passphrase The given passphrase.
* @param pgpContacts A list of contacts.
* @return A result with an instance of [GenerateKeyResult]
*/
suspend fun createPrivateKey(context: Context, passphrase: String, pgpContacts: List<PgpContact>): Result<GenerateKeyResult?>


/**
* Check the passphrase strong value.
*
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
*/

package com.flowcrypt.email.extensions

import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.key.info.KeyRingInfo

/**
* @author Denis Bondarenko
* Date: 3/11/21
* Time: 10:33 AM
* E-mail: DenBond7@gmail.com
*/

/**
* Return true when every secret key on the key ring is encrypted.
* If there is at least one unencrypted secret key on the ring, return false.
* If the ring is a [PGPPublicKeyRing], return false.
*
* @return true if all secret keys are encrypted.
*/
fun KeyRingInfo.isFullyEncrypted(): Boolean {
if (isSecretKey) {
for (secretKey in secretKeys) {
if (secretKey.keyEncryptionAlgorithm == SymmetricKeyAlgorithm.NULL.algorithmId) {
return false
}
Comment on lines +28 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to cross check this isFullyEncrypted - for one thing there is a dummy gnu S2K requiring special treatment, second there could be some null-like encryption method like plain. (sometimes the spec is messy). That will be for another PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

return true
} else return false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
* Contributors: DenBond7
*/

package com.flowcrypt.email.extensions

import com.flowcrypt.email.api.retrofit.response.model.node.Algo
import com.flowcrypt.email.api.retrofit.response.model.node.KeyId
import com.flowcrypt.email.api.retrofit.response.model.node.NodeKeyDetails
import com.flowcrypt.email.security.pgp.PgpArmorUtils
import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.pgpainless.algorithm.PublicKeyAlgorithm
import org.pgpainless.key.OpenPgpV4Fingerprint
import org.pgpainless.key.generation.type.eddsa.EdDSACurve
import org.pgpainless.key.info.KeyInfo
import org.pgpainless.key.info.KeyRingInfo
import org.pgpainless.key.util.KeyRingUtils
import java.util.concurrent.TimeUnit

/**
* @author Denis Bondarenko
* Date: 3/11/21
* Time: 10:08 AM
* E-mail: DenBond7@gmail.com
*/
fun PGPKeyRing.toNodeKeyDetails(): NodeKeyDetails {
val keyRingInfo = KeyRingInfo(this)

val algo = Algo(
algorithm = keyRingInfo.algorithm.name,
algorithmId = keyRingInfo.algorithm.algorithmId,
bits = if (keyRingInfo.publicKey.bitStrength != -1) keyRingInfo.publicKey.bitStrength else 0,
curve = when (keyRingInfo.algorithm) {
PublicKeyAlgorithm.ECDSA, PublicKeyAlgorithm.ECDH -> KeyInfo.getCurveName(publicKey)
PublicKeyAlgorithm.EDDSA -> EdDSACurve._Ed25519.getName() // for EDDSA KeyInfo.getCurveName(publicKey) return null
else -> null
}
)

val ids = publicKeys.iterator().asSequence().toList()
.map {
val fingerprint = OpenPgpV4Fingerprint(it)
KeyId(
fingerprint = fingerprint.toString(),
longId = fingerprint.takeLast(16).toString(),
shortId = fingerprint.takeLast(8).toString(),
keywords = ""//skipped as deprecated
)
}

val privateKey = if (keyRingInfo.isSecretKey) PgpArmorUtils.toAsciiArmoredString(this) else null
val publicKey = if (keyRingInfo.isSecretKey) {
PgpArmorUtils.toAsciiArmoredString(KeyRingUtils.publicKeyRingFrom(this as PGPSecretKeyRing?))
} else {
PgpArmorUtils.toAsciiArmoredString(this)
}

return NodeKeyDetails(
isFullyDecrypted = keyRingInfo.isFullyDecrypted,
isFullyEncrypted = keyRingInfo.isFullyEncrypted(),
privateKey = privateKey,
publicKey = publicKey,
users = keyRingInfo.userIds,
ids = ids,
created = TimeUnit.SECONDS.convert(keyRingInfo.creationDate.time, TimeUnit.MILLISECONDS),
lastModified = TimeUnit.SECONDS.convert(keyRingInfo.lastModified.time, TimeUnit.MILLISECONDS),
expiration = TimeUnit.SECONDS.convert(keyRingInfo.expirationDate?.time
?: 0, TimeUnit.MILLISECONDS),
algo = algo,
passphrase = null,
errorMsg = null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.flowcrypt.email.api.retrofit.response.model.node.NodeKeyDetails
import com.flowcrypt.email.api.retrofit.response.node.ParseKeysResult
import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.database.entity.ActionQueueEntity
import com.flowcrypt.email.extensions.toNodeKeyDetails
import com.flowcrypt.email.model.KeyDetails
import com.flowcrypt.email.model.KeyImportModel
import com.flowcrypt.email.model.PgpContact
Expand All @@ -51,6 +52,8 @@ import com.google.android.gms.common.util.CollectionUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.pgpainless.PGPainless
import org.pgpainless.key.util.UserId
import java.util.*

/**
Expand Down Expand Up @@ -261,8 +264,9 @@ class PrivateKeysViewModel(application: Application) : BaseNodeApiViewModel(appl
createPrivateKeyLiveData.value = Result.loading()
var nodeKeyDetails: NodeKeyDetails? = null
try {
nodeKeyDetails = genPrivateKeyViaNode(passphrase, accountEntity)
requireNotNull(nodeKeyDetails)
nodeKeyDetails = PGPainless.generateKeyRing().simpleEcKeyRing(
UserId.nameAndEmail(accountEntity.displayName
?: accountEntity.email, accountEntity.email), passphrase).toNodeKeyDetails()

val existedAccount = roomDatabase.accountDao().getAccountSuspend(accountEntity.email.toLowerCase(Locale.US))
if (existedAccount == null) {
Expand Down Expand Up @@ -307,28 +311,6 @@ class PrivateKeysViewModel(application: Application) : BaseNodeApiViewModel(appl
}
}

private suspend fun genPrivateKeyViaNode(passphrase: String, accountEntity: AccountEntity): NodeKeyDetails? {
val generateKeyResult = nodeRepository.createPrivateKey(getApplication(), passphrase, genContacts(accountEntity))
when (generateKeyResult.status) {
Result.Status.SUCCESS -> {
return generateKeyResult.data?.key
}

Result.Status.EXCEPTION -> {
generateKeyResult.exception?.let { exception -> throw exception }
}

Result.Status.ERROR -> {
generateKeyResult.data?.apiError?.let { apiError -> throw ApiException(apiError) }
}

else -> {
// all looks well
}
}
return null
}

private suspend fun savePrivateKeyToDatabase(accountEntity: AccountEntity, nodeKeyDetails: NodeKeyDetails, passphrase: String) {
val keyEntity = nodeKeyDetails.toKeyEntity(accountEntity).copy(
source = KeyDetails.Type.NEW.toPrivateKeySourceTypeString(),
Expand Down
Loading