Skip to content

Commit

Permalink
Added signature verification for cleartext.| #1097
Browse files Browse the repository at this point in the history
  • Loading branch information
DenBond7 committed Dec 13, 2021
1 parent 0e8aa35 commit cd28e99
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package com.flowcrypt.email.api.retrofit.response.model
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import org.pgpainless.decryption_verification.OpenPgpMetadata

/**
* Message block which represents content with a signature.
Expand All @@ -21,6 +22,8 @@ data class SignedMsgBlock(
@Expose override val error: MsgBlockError? = null
) : MsgBlock {

var openPgpMetadata: OpenPgpMetadata? = null

@Expose
override val type: MsgBlock.Type = when (signedType) {
Type.SIGNED_MSG -> MsgBlock.Type.SIGNED_MSG
Expand Down
69 changes: 49 additions & 20 deletions FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt
Original file line number Diff line number Diff line change
Expand Up @@ -828,31 +828,50 @@ object PgpMsg {
// So, at least meanwhile, not porting this:
// block.content = isContentBlock(block.type)
// ? block.content.toUtfStr() : block.content.toRawBytesStr();
if (block is DecryptedAndOrSignedContentMsgBlock) {
if (block.type in listOf(
MsgBlock.Type.SIGNED_MSG,
MsgBlock.Type.DECRYPTED_AND_OR_SIGNED_CONTENT
)
) {
val openPgpMetadata = when (block) {
is DecryptedAndOrSignedContentMsgBlock -> {
block.openPgpMetadata
}

is SignedMsgBlock -> {
block.openPgpMetadata
}

else -> null
}

if (!isEncrypted) {
isEncrypted = block.openPgpMetadata?.isEncrypted ?: false
isEncrypted = openPgpMetadata?.isEncrypted ?: false
}

if (block.openPgpMetadata?.isSigned == true) {
if (openPgpMetadata?.isSigned == true) {
signedBlockCount++
block.openPgpMetadata?.let { openPgpMetadata ->
if (openPgpMetadata.invalidInbandSignatures.isNotEmpty()
|| openPgpMetadata.invalidDetachedSignatures.isNotEmpty()
) {
hasUnverifiedSignatures = true
}
if (verifiedSignatures.isEmpty()) {

if (openPgpMetadata.invalidInbandSignatures.isNotEmpty()
|| openPgpMetadata.invalidDetachedSignatures.isNotEmpty()
) {
hasUnverifiedSignatures = true
}

if (verifiedSignatures.isEmpty()) {
verifiedSignatures.putAll(openPgpMetadata.verifiedSignatures)
} else {
val bufferedKeysIds = verifiedSignatures.keys.map { it.keyId }
val iteratedKeysIds = openPgpMetadata.verifiedSignatures.keys.map { it.keyId }
if (bufferedKeysIds != iteratedKeysIds) {
hasMixedSignatures = true
verifiedSignatures.putAll(openPgpMetadata.verifiedSignatures)
} else {
if (verifiedSignatures.keys.map { it.keyId } != openPgpMetadata.verifiedSignatures.keys.map { it.keyId }) {
hasMixedSignatures = true
//todo-denbond7 need to check it
verifiedSignatures.putAll(openPgpMetadata.verifiedSignatures)
}
}
}
}
}

if (block is DecryptedAndOrSignedContentMsgBlock) {
for (innerBlock in block.blocks) {
if (canBeAddedToCombinedContent(innerBlock)) {
contentBlocks.add(innerBlock)
Expand Down Expand Up @@ -909,7 +928,9 @@ object PgpMsg {
for (msgBlock in msgBlocks) {
when {
msgBlock is SignedMsgBlock -> {
processSignedMsgBlock(msgBlock)?.let { sequentialProcessedBlocks.add(it) }
processSignedMsgBlock(msgBlock, pgpPublicKeyRingCollection)?.let {
sequentialProcessedBlocks.add(it)
}
}

msgBlock.type == MsgBlock.Type.ENCRYPTED_MSG -> {
Expand Down Expand Up @@ -1078,7 +1099,10 @@ object PgpMsg {
}
}

private fun processSignedMsgBlock(msgBlock: SignedMsgBlock): MsgBlock? {
private fun processSignedMsgBlock(
msgBlock: SignedMsgBlock,
pgpPublicKeyRingCollection: PGPPublicKeyRingCollection
): MsgBlock? {
return when {
msgBlock.signature != null -> {
when (msgBlock.type) {
Expand Down Expand Up @@ -1114,8 +1138,13 @@ object PgpMsg {

msgBlock.type == MsgBlock.Type.SIGNED_MSG -> {
return try {
val cleartext = PgpSignature.extractClearText(msgBlock.content, false)
msgBlock.copy(content = cleartext)
val clearTextVerificationResult = PgpSignature.verifyClearTextSignature(
srcInputStream = requireNotNull(msgBlock.content?.toInputStream()),
pgpPublicKeyRingCollection = pgpPublicKeyRingCollection
)
clearTextVerificationResult.exception?.let { throw it }
msgBlock.copy(content = clearTextVerificationResult.clearText)
.apply { openPgpMetadata = clearTextVerificationResult.openPgpMetadata }
} catch (e: Exception) {
msgBlock.copy(error = MsgBlockError("[" + e.javaClass.simpleName + "]: " + e.message))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
package com.flowcrypt.email.security.pgp


import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
import org.pgpainless.PGPainless
import org.pgpainless.decryption_verification.ConsumerOptions
import org.pgpainless.decryption_verification.OpenPgpMetadata
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream

/**
Expand Down Expand Up @@ -40,4 +46,33 @@ object PgpSignature {
}
}
}

fun verifyClearTextSignature(
srcInputStream: InputStream,
pgpPublicKeyRingCollection: PGPPublicKeyRingCollection
): ClearTextVerificationResult {
ByteArrayOutputStream().use { outStream ->
return try {
val verificationStream = PGPainless.verifyCleartextSignedMessage()
.onInputStream(srcInputStream)
.withStrategy(InMemoryMultiPassStrategy())
.withOptions(ConsumerOptions().addVerificationCerts(pgpPublicKeyRingCollection))
.verificationStream

verificationStream.use { it.copyTo(outStream) }
ClearTextVerificationResult(
openPgpMetadata = verificationStream.result,
clearText = String(outStream.toByteArray())
)
} catch (e: Exception) {
ClearTextVerificationResult(exception = e)
}
}
}

data class ClearTextVerificationResult(
val openPgpMetadata: OpenPgpMetadata? = null,
val clearText: String? = null,
val exception: Exception? = null
)
}

0 comments on commit cd28e99

Please sign in to comment.