pgpainless/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt

1018 lines
40 KiB
Kotlin

// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import openpgp.openPgpKeyId
import org.bouncycastle.bcpg.BCPGInputStream
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
import org.bouncycastle.extensions.getPublicKeyFor
import org.bouncycastle.extensions.getSecretKeyFor
import org.bouncycastle.extensions.issuerKeyId
import org.bouncycastle.extensions.unlock
import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.bouncycastle.util.io.TeeInputStream
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.*
import org.pgpainless.decryption_verification.MessageMetadata.*
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil
import org.pgpainless.decryption_verification.syntax_check.InputSymbol
import org.pgpainless.decryption_verification.syntax_check.PDA
import org.pgpainless.decryption_verification.syntax_check.StackSymbol
import org.pgpainless.exception.*
import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.CertificateValidator
import org.pgpainless.signature.consumer.OnePassSignatureCheck
import org.pgpainless.signature.consumer.SignatureCheck
import org.pgpainless.signature.consumer.SignatureValidator
import org.pgpainless.util.ArmoredInputStreamFactory
import org.pgpainless.util.SessionKey
import org.slf4j.LoggerFactory
class OpenPgpMessageInputStream(
type: Type,
inputStream: InputStream,
private val options: ConsumerOptions,
private val layerMetadata: Layer,
private val policy: Policy
) : DecryptionStream() {
private val signatures: Signatures = Signatures(options)
private var packetInputStream: TeeBCPGInputStream? = null
private var nestedInputStream: InputStream? = null
private val syntaxVerifier = PDA()
private var closed = false
init {
// Add detached signatures only on the outermost OpenPgpMessageInputStream
if (layerMetadata is Message) {
signatures.addDetachedSignatures(options.getDetachedSignatures())
}
when (type) {
Type.standard -> {
// tee out packet bytes for signature verification
packetInputStream =
TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures)
// *omnomnom*
consumePackets()
}
Type.cleartext_signed -> {
val multiPassStrategy = options.getMultiPassStrategy()
val detachedSignatures =
ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(
inputStream, multiPassStrategy.messageOutputStream)
for (signature in detachedSignatures) {
signatures.addDetachedSignature(signature)
}
options.isForceNonOpenPgpData()
nestedInputStream =
TeeInputStream(multiPassStrategy.messageInputStream, this.signatures)
}
Type.non_openpgp -> {
packetInputStream = null
nestedInputStream = TeeInputStream(inputStream, this.signatures)
}
}
}
enum class Type {
standard,
cleartext_signed,
non_openpgp
}
constructor(
inputStream: InputStream,
options: ConsumerOptions,
metadata: Layer,
policy: Policy
) : this(Type.standard, inputStream, options, metadata, policy)
private fun consumePackets() {
val pIn = packetInputStream ?: return
var packet: OpenPgpPacket?
// Comsume packets, potentially stepping into nested layers
layer@ while (run {
packet = pIn.nextPacketTag()
packet
} != null) {
signatures.nextPacket(packet!!)
// Consume packets in a layer
when (packet) {
OpenPgpPacket.LIT -> {
processLiteralData()
break@layer // nest down
}
OpenPgpPacket.COMP -> {
processCompressedData()
break@layer // nest down
}
OpenPgpPacket.OPS -> {
processOnePassSignature() // OPS is on the same layer, no nest down
}
OpenPgpPacket.SIG -> {
processSignature() // SIG is on the same layer, no nest down
}
OpenPgpPacket.PKESK,
OpenPgpPacket.SKESK,
OpenPgpPacket.SED,
OpenPgpPacket.SEIPD -> {
if (processEncryptedData()) {
break@layer
}
throw MissingDecryptionMethodException("No working decryption method found.")
}
OpenPgpPacket.MARKER -> {
LOGGER.debug("Skipping Marker Packet")
pIn.readMarker()
}
OpenPgpPacket.SK,
OpenPgpPacket.PK,
OpenPgpPacket.SSK,
OpenPgpPacket.PSK,
OpenPgpPacket.TRUST,
OpenPgpPacket.UID,
OpenPgpPacket.UATTR ->
throw MalformedOpenPgpMessageException("Illegal Packet in Stream: $packet")
OpenPgpPacket.EXP_1,
OpenPgpPacket.EXP_2,
OpenPgpPacket.EXP_3,
OpenPgpPacket.EXP_4 ->
throw MalformedOpenPgpMessageException("Unsupported Packet in Stream: $packet")
else ->
throw MalformedOpenPgpMessageException("Unexpected Packet in Stream: $packet")
}
}
}
private fun processLiteralData() {
LOGGER.debug("Literal Data Packet at depth ${layerMetadata.depth} encountered.")
syntaxVerifier.next(InputSymbol.LITERAL_DATA)
val literalData = packetInputStream!!.readLiteralData()
// Extract Metadata
layerMetadata.child =
LiteralData(
literalData.fileName,
literalData.modificationTime,
StreamEncoding.requireFromCode(literalData.format))
nestedInputStream = literalData.inputStream
}
private fun processCompressedData() {
syntaxVerifier.next(InputSymbol.COMPRESSED_DATA)
signatures.enterNesting()
val compressedData = packetInputStream!!.readCompressedData()
// Extract Metadata
val compressionLayer =
CompressedData(
CompressionAlgorithm.requireFromId(compressedData.algorithm),
layerMetadata.depth + 1)
LOGGER.debug(
"Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.")
nestedInputStream =
OpenPgpMessageInputStream(compressedData.dataStream, options, compressionLayer, policy)
}
private fun processOnePassSignature() {
syntaxVerifier.next(InputSymbol.ONE_PASS_SIGNATURE)
val ops = packetInputStream!!.readOnePassSignature()
LOGGER.debug(
"One-Pass-Signature Packet by key ${ops.keyID.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
signatures.addOnePassSignature(ops)
}
private fun processSignature() {
// true if signature corresponds to OPS
val isSigForOps = syntaxVerifier.peekStack() == StackSymbol.OPS
syntaxVerifier.next(InputSymbol.SIGNATURE)
val signature =
try {
packetInputStream!!.readSignature()
} catch (e: UnsupportedPacketVersionException) {
LOGGER.debug(
"Unsupported Signature at depth ${layerMetadata.depth} encountered.", e)
return
}
val keyId = signature.issuerKeyId
if (isSigForOps) {
LOGGER.debug(
"Signature Packet corresponding to One-Pass-Signature by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
signatures
.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are
// dealt with
signatures.addCorrespondingOnePassSignature(signature, layerMetadata, policy)
} else {
LOGGER.debug(
"Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
signatures.addPrependedSignature(signature)
}
}
private fun processEncryptedData(): Boolean {
LOGGER.debug(
"Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered.")
syntaxVerifier.next(InputSymbol.ENCRYPTED_DATA)
val encDataList = packetInputStream!!.readEncryptedDataList()
if (!encDataList.isIntegrityProtected) {
LOGGER.warn("Symmetrically Encrypted Data Packet is not integrity-protected.")
if (!options.isIgnoreMDCErrors()) {
throw MessageNotIntegrityProtectedException()
}
}
val esks = SortedESKs(encDataList)
LOGGER.debug(
"Symmetrically Encrypted Integrity-Protected Data has ${esks.skesks.size} SKESK(s) and" +
" ${esks.pkesks.size + esks.anonPkesks.size} PKESK(s) from which ${esks.anonPkesks.size} PKESK(s)" +
" have an anonymous recipient.")
// try custom decryptor factories
for ((key, decryptorFactory) in options.getCustomDecryptorFactories()) {
LOGGER.debug("Attempt decryption with custom decryptor factory with key $key.")
esks.pkesks
.filter {
// find matching PKESK
it.keyID == key.subkeyId
}
.forEach {
// attempt decryption
if (decryptPKESKAndStream(esks, key, decryptorFactory, it)) {
return true
}
}
}
// try provided session key
if (options.getSessionKey() != null) {
val sk = options.getSessionKey()!!
LOGGER.debug("Attempt decryption with provided session key.")
throwIfUnacceptable(sk.algorithm)
val decryptorFactory =
ImplementationFactory.getInstance().getSessionKeyDataDecryptorFactory(sk)
val layer = EncryptedData(sk.algorithm, layerMetadata.depth + 1)
val skEncData = encDataList.extractSessionKeyEncryptedData()
try {
val decrypted = skEncData.getDataStream(decryptorFactory)
layer.sessionKey = sk
val integrityProtected =
IntegrityProtectedInputStream(decrypted, skEncData, options)
nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, layer, policy)
LOGGER.debug("Successfully decrypted data using provided session key")
return true
} catch (e: PGPException) {
// Session key mismatch?
LOGGER.debug(
"Decryption using provided session key failed. Mismatched session key and message?",
e)
}
}
// try passwords
for (passphrase in options.getDecryptionPassphrases()) {
for (skesk in esks.skesks) {
LOGGER.debug("Attempt decryption with provided passphrase")
val algorithm = SymmetricKeyAlgorithm.requireFromId(skesk.algorithm)
if (!isAcceptable(algorithm)) {
LOGGER.debug(
"Skipping SKESK with unacceptable encapsulation algorithm $algorithm")
continue
}
val decryptorFactory =
ImplementationFactory.getInstance().getPBEDataDecryptorFactory(passphrase)
if (decryptSKESKAndStream(esks, skesk, decryptorFactory)) {
return true
}
}
}
val postponedDueToMissingPassphrase =
mutableListOf<Pair<PGPSecretKey, PGPPublicKeyEncryptedData>>()
// try (known) secret keys
esks.pkesks.forEach { pkesk ->
LOGGER.debug("Encountered PKESK for recipient ${pkesk.keyID.openPgpKeyId()}")
val decryptionKeyCandidates = getDecryptionKeys(pkesk)
for (decryptionKeys in decryptionKeyCandidates) {
val secretKey = decryptionKeys.getSecretKeyFor(pkesk)!!
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, secretKey.keyID)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue
}
LOGGER.debug("Attempt decryption using secret key $decryptionKeyId")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
if (!protector.hasPassphraseFor(secretKey.keyID)) {
LOGGER.debug(
"Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.")
postponedDueToMissingPassphrase.add(secretKey to pkesk)
continue
}
val privateKey = secretKey.unlock(protector)
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
return true
}
}
}
// try anonymous secret keys
for (pkesk in esks.anonPkesks) {
for ((decryptionKeys, secretKey) in findPotentialDecryptionKeys(pkesk)) {
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, secretKey.keyID)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue
}
LOGGER.debug("Attempt decryption of anonymous PKESK with key $decryptionKeyId.")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
if (!protector.hasPassphraseFor(secretKey.keyID)) {
LOGGER.debug(
"Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.")
postponedDueToMissingPassphrase.add(secretKey to pkesk)
continue
}
val privateKey = secretKey.unlock(protector)
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
return true
}
}
}
if (options.getMissingKeyPassphraseStrategy() ==
MissingKeyPassphraseStrategy.THROW_EXCEPTION) {
// Non-interactive mode: Throw an exception with all locked decryption keys
postponedDueToMissingPassphrase
.map { SubkeyIdentifier(getDecryptionKey(it.first.keyID)!!, it.first.keyID) }
.also { if (it.isNotEmpty()) throw MissingPassphraseException(it.toSet()) }
} else if (options.getMissingKeyPassphraseStrategy() ==
MissingKeyPassphraseStrategy.INTERACTIVE) {
for ((secretKey, pkesk) in postponedDueToMissingPassphrase) {
val keyId = secretKey.keyID
val decryptionKeys = getDecryptionKey(pkesk)!!
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, keyId)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue
}
LOGGER.debug(
"Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
val privateKey = secretKey.unlock(protector)
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
return true
}
}
} else {
throw IllegalStateException("Invalid PostponedKeysStrategy set in consumer options.")
}
// We did not yet succeed in decrypting any session key :/
LOGGER.debug("Failed to decrypt encrypted data packet.")
return false
}
private fun decryptWithPrivateKey(
esks: SortedESKs,
privateKey: PGPPrivateKey,
decryptionKeyId: SubkeyIdentifier,
pkesk: PGPPublicKeyEncryptedData
): Boolean {
val decryptorFactory =
ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey)
return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk)
}
private fun hasUnsupportedS2KSpecifier(
secretKey: PGPSecretKey,
decryptionKeyId: SubkeyIdentifier
): Boolean {
val s2k = secretKey.s2K
if (s2k != null) {
if (s2k.type in 100..110) {
LOGGER.debug(
"Skipping PKESK because key $decryptionKeyId has unsupported private S2K specifier ${s2k.type}")
return true
}
}
return false
}
private fun decryptSKESKAndStream(
esks: SortedESKs,
skesk: PGPPBEEncryptedData,
decryptorFactory: PBEDataDecryptorFactory
): Boolean {
try {
val decrypted = skesk.getDataStream(decryptorFactory)
val sessionKey = SessionKey(skesk.getSessionKey(decryptorFactory))
throwIfUnacceptable(sessionKey.algorithm)
val encryptedData = EncryptedData(sessionKey.algorithm, layerMetadata.depth + 1)
encryptedData.sessionKey = sessionKey
encryptedData.addRecipients(esks.pkesks.map { it.keyID })
LOGGER.debug("Successfully decrypted data with passphrase")
val integrityProtected = IntegrityProtectedInputStream(decrypted, skesk, options)
nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy)
return true
} catch (e: UnacceptableAlgorithmException) {
throw e
} catch (e: PGPException) {
LOGGER.debug(
"Decryption of encrypted data packet using password failed. Password mismatch?", e)
}
return false
}
private fun decryptPKESKAndStream(
esks: SortedESKs,
decryptionKeyId: SubkeyIdentifier,
decryptorFactory: PublicKeyDataDecryptorFactory,
pkesk: PGPPublicKeyEncryptedData
): Boolean {
try {
val decrypted = pkesk.getDataStream(decryptorFactory)
val sessionKey = SessionKey(pkesk.getSessionKey(decryptorFactory))
throwIfUnacceptable(sessionKey.algorithm)
val encryptedData =
EncryptedData(
SymmetricKeyAlgorithm.requireFromId(
pkesk.getSymmetricAlgorithm(decryptorFactory)),
layerMetadata.depth + 1)
encryptedData.decryptionKey = decryptionKeyId
encryptedData.sessionKey = sessionKey
encryptedData.addRecipients(esks.pkesks.plus(esks.anonPkesks).map { it.keyID })
LOGGER.debug("Successfully decrypted data with key $decryptionKeyId")
val integrityProtected = IntegrityProtectedInputStream(decrypted, pkesk, options)
nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy)
return true
} catch (e: UnacceptableAlgorithmException) {
throw e
} catch (e: PGPException) {
LOGGER.debug("Decryption of encrypted data packet using secret key failed.", e)
}
return false
}
override fun read(): Int {
if (nestedInputStream == null) {
if (packetInputStream != null) {
syntaxVerifier.next(InputSymbol.END_OF_SEQUENCE)
syntaxVerifier.assertValid()
}
return -1
}
val r: Int =
try {
nestedInputStream!!.read()
} catch (e: IOException) {
-1
}
if (r != -1) {
signatures.updateLiteral(r.toByte())
} else {
nestedInputStream!!.close()
collectMetadata()
nestedInputStream = null
if (packetInputStream != null) {
try {
consumePackets()
} catch (e: PGPException) {
throw RuntimeException(e)
}
}
signatures.finish(layerMetadata, policy)
}
return r
}
override fun read(b: ByteArray, off: Int, len: Int): Int {
if (nestedInputStream == null) {
if (packetInputStream != null) {
syntaxVerifier.next(InputSymbol.END_OF_SEQUENCE)
syntaxVerifier.assertValid()
}
return -1
}
val r = nestedInputStream!!.read(b, off, len)
if (r != -1) {
signatures.updateLiteral(b, off, r)
} else {
nestedInputStream!!.close()
collectMetadata()
nestedInputStream = null
if (packetInputStream != null) {
try {
consumePackets()
} catch (e: PGPException) {
throw RuntimeException(e)
}
}
signatures.finish(layerMetadata, policy)
}
return r
}
override fun close() {
super.close()
if (closed) {
if (packetInputStream != null) {
syntaxVerifier.next(InputSymbol.END_OF_SEQUENCE)
syntaxVerifier.assertValid()
}
return
}
if (nestedInputStream != null) {
nestedInputStream!!.close()
collectMetadata()
nestedInputStream = null
}
try {
consumePackets()
} catch (e: PGPException) {
throw RuntimeException(e)
}
if (packetInputStream != null) {
syntaxVerifier.next(InputSymbol.END_OF_SEQUENCE)
syntaxVerifier.assertValid()
packetInputStream!!.close()
}
closed = true
}
private fun collectMetadata() {
if (nestedInputStream is OpenPgpMessageInputStream) {
val child = nestedInputStream as OpenPgpMessageInputStream
layerMetadata.child = (child.layerMetadata as Nested)
}
}
override val metadata: MessageMetadata
get() {
check(closed) { "Stream must be closed before access to metadata can be granted." }
return MessageMetadata((layerMetadata as Message))
}
private fun getDecryptionKey(keyId: Long): PGPSecretKeyRing? =
options.getDecryptionKeys().firstOrNull {
it.any { k -> k.keyID == keyId }
.and(PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k -> k.keyID == keyId })
}
private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): PGPSecretKeyRing? =
options.getDecryptionKeys().firstOrNull {
it.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
when (pkesk.version) {
3 -> pkesk.keyID == subkey.keyID
else -> throw NotImplementedError("Version 6 PKESK not yet supported.")
}
}
}
private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<PGPSecretKeyRing> =
options.getDecryptionKeys().filter {
it.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
when (pkesk.version) {
3 -> pkesk.keyID == subkey.keyID
else -> throw NotImplementedError("Version 6 PKESK not yet supported.")
}
}
}
private fun findPotentialDecryptionKeys(
pkesk: PGPPublicKeyEncryptedData
): List<Pair<PGPSecretKeyRing, PGPSecretKey>> {
val algorithm = pkesk.algorithm
val candidates = mutableListOf<Pair<PGPSecretKeyRing, PGPSecretKey>>()
options.getDecryptionKeys().forEach {
val info = PGPainless.inspectKeyRing(it)
for (key in info.decryptionSubkeys) {
if (key.algorithm == algorithm && info.isSecretKeyAvailable(key.keyID)) {
candidates.add(it to it.getSecretKey(key.keyID))
}
}
}
return candidates
}
private fun isAcceptable(algorithm: SymmetricKeyAlgorithm): Boolean =
policy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm)
private fun throwIfUnacceptable(algorithm: SymmetricKeyAlgorithm) {
if (!isAcceptable(algorithm)) {
throw UnacceptableAlgorithmException(
"Symmetric-Key algorithm $algorithm is not acceptable for message decryption.")
}
}
private class SortedESKs(esks: PGPEncryptedDataList) {
val skesks: List<PGPPBEEncryptedData>
val pkesks: List<PGPPublicKeyEncryptedData>
val anonPkesks: List<PGPPublicKeyEncryptedData>
init {
skesks = mutableListOf()
pkesks = mutableListOf()
anonPkesks = mutableListOf()
for (esk in esks) {
if (esk is PGPPBEEncryptedData) {
skesks.add(esk)
} else if (esk is PGPPublicKeyEncryptedData) {
if (esk.keyID != 0L) {
pkesks.add(esk)
} else {
anonPkesks.add(esk)
}
} else {
throw IllegalArgumentException("Unknown ESK class type ${esk.javaClass}")
}
}
}
val all: List<PGPEncryptedData>
get() = skesks.plus(pkesks).plus(anonPkesks)
}
private class Signatures(val options: ConsumerOptions) : OutputStream() {
val detachedSignatures = mutableListOf<SignatureCheck>()
val prependedSignatures = mutableListOf<SignatureCheck>()
val onePassSignatures = mutableListOf<OnePassSignatureCheck>()
val opsUpdateStack = ArrayDeque<MutableList<OnePassSignatureCheck>>()
var literalOPS = mutableListOf<OnePassSignatureCheck>()
val correspondingSignatures = mutableListOf<PGPSignature>()
val prependedSignaturesWithMissingCert = mutableListOf<SignatureVerification.Failure>()
val inbandSignaturesWithMissingCert = mutableListOf<SignatureVerification.Failure>()
val detachedSignaturesWithMissingCert = mutableListOf<SignatureVerification.Failure>()
var isLiteral = true
fun addDetachedSignatures(signatures: Collection<PGPSignature>) {
for (signature in signatures) {
addDetachedSignature(signature)
}
}
fun addDetachedSignature(signature: PGPSignature) {
val check = initializeSignature(signature)
val keyId = signature.issuerKeyId
if (check != null) {
detachedSignatures.add(check)
} else {
LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
detachedSignaturesWithMissingCert.add(
SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key.")))
}
}
fun addPrependedSignature(signature: PGPSignature) {
val check = initializeSignature(signature)
val keyId = signature.issuerKeyId
if (check != null) {
prependedSignatures.add(check)
} else {
LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
prependedSignaturesWithMissingCert.add(
SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key")))
}
}
fun initializeSignature(signature: PGPSignature): SignatureCheck? {
val certificate = findCertificate(signature) ?: return null
val publicKey = certificate.getPublicKeyFor(signature) ?: return null
val verifierKey = SubkeyIdentifier(certificate, publicKey.keyID)
initialize(signature, publicKey)
return SignatureCheck(signature, certificate, verifierKey)
}
fun addOnePassSignature(signature: PGPOnePassSignature) {
val certificate = findCertificate(signature)
if (certificate != null) {
val publicKey = certificate.getPublicKeyFor(signature)
if (publicKey != null) {
val ops = OnePassSignatureCheck(signature, certificate)
initialize(signature, publicKey)
onePassSignatures.add(ops)
literalOPS.add(ops)
}
}
if (signature.isContaining) {
enterNesting()
}
}
fun addCorrespondingOnePassSignature(
signature: PGPSignature,
layer: Layer,
policy: Policy
) {
var found = false
val keyId = signature.issuerKeyId
for ((i, check) in onePassSignatures.withIndex().reversed()) {
if (check.onePassSignature.keyID != keyId) {
continue
}
found = true
if (check.signature != null) {
continue
}
check.signature = signature
val verification =
SignatureVerification(
signature,
SubkeyIdentifier(check.verificationKeys, check.onePassSignature.keyID))
try {
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(signature)
CertificateValidator.validateCertificateAndVerifyOnePassSignature(check, policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedOnePassSignature(verification)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedOnePassSignature(
SignatureVerification.Failure(verification, e))
}
break
}
if (!found) {
LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
inbandSignaturesWithMissingCert.add(
SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key.")))
}
}
fun enterNesting() {
opsUpdateStack.addFirst(literalOPS)
literalOPS = mutableListOf()
}
fun leaveNesting() {
if (opsUpdateStack.isEmpty()) {
return
}
opsUpdateStack.removeFirst()
}
private fun findCertificate(signature: PGPSignature): PGPPublicKeyRing? {
val cert = options.getCertificateSource().getCertificate(signature)
if (cert != null) {
return cert
}
if (options.getMissingCertificateCallback() != null) {
return options
.getMissingCertificateCallback()!!
.onMissingPublicKeyEncountered(signature.keyID)
}
return null // TODO: Missing cert for sig
}
private fun findCertificate(signature: PGPOnePassSignature): PGPPublicKeyRing? {
val cert = options.getCertificateSource().getCertificate(signature.keyID)
if (cert != null) {
return cert
}
if (options.getMissingCertificateCallback() != null) {
return options
.getMissingCertificateCallback()!!
.onMissingPublicKeyEncountered(signature.keyID)
}
return null // TODO: Missing cert for sig
}
fun updateLiteral(b: Byte) {
for (ops in literalOPS) {
ops.onePassSignature.update(b)
}
for (detached in detachedSignatures) {
detached.signature.update(b)
}
for (prepended in prependedSignatures) {
prepended.signature.update(b)
}
}
fun updateLiteral(buf: ByteArray, off: Int, len: Int) {
for (ops in literalOPS) {
ops.onePassSignature.update(buf, off, len)
}
for (detached in detachedSignatures) {
detached.signature.update(buf, off, len)
}
for (prepended in prependedSignatures) {
prepended.signature.update(buf, off, len)
}
}
fun updatePacket(b: Byte) {
for (nestedOPSs in opsUpdateStack.reversed()) {
for (ops in nestedOPSs) {
ops.onePassSignature.update(b)
}
}
}
fun updatePacket(buf: ByteArray, off: Int, len: Int) {
for (nestedOPSs in opsUpdateStack.reversed()) {
for (ops in nestedOPSs) {
ops.onePassSignature.update(buf, off, len)
}
}
}
fun finish(layer: Layer, policy: Policy) {
for (detached in detachedSignatures) {
val verification =
SignatureVerification(detached.signature, detached.signingKeyIdentifier)
try {
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(detached.signature)
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
detached.signature,
KeyRingUtils.publicKeys(detached.signingKeyRing),
policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedDetachedSignature(verification)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedDetachedSignature(
SignatureVerification.Failure(verification, e))
}
}
for (prepended in prependedSignatures) {
val verification =
SignatureVerification(prepended.signature, prepended.signingKeyIdentifier)
try {
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(prepended.signature)
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
prepended.signature,
KeyRingUtils.publicKeys(prepended.signingKeyRing),
policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedPrependedSignature(verification)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedPrependedSignature(
SignatureVerification.Failure(verification, e))
}
}
for (rejected in inbandSignaturesWithMissingCert) {
layer.addRejectedOnePassSignature(rejected)
}
for (rejected in prependedSignaturesWithMissingCert) {
layer.addRejectedPrependedSignature(rejected)
}
for (rejected in detachedSignaturesWithMissingCert) {
layer.addRejectedDetachedSignature(rejected)
}
}
override fun write(b: Int) {
updatePacket(b.toByte())
}
override fun write(buf: ByteArray, off: Int, len: Int) {
updatePacket(buf, off, len)
}
fun nextPacket(nextPacket: OpenPgpPacket) {
if (nextPacket == OpenPgpPacket.LIT) {
isLiteral = true
if (literalOPS.isEmpty() && opsUpdateStack.isNotEmpty()) {
literalOPS = opsUpdateStack.removeFirst()
}
} else {
isLiteral = false
}
}
companion object {
@JvmStatic
private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) {
val verifierProvider =
ImplementationFactory.getInstance().pgpContentVerifierBuilderProvider
try {
signature.init(verifierProvider, publicKey)
} catch (e: PGPException) {
throw RuntimeException(e)
}
}
@JvmStatic
private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) {
val verifierProvider =
ImplementationFactory.getInstance().pgpContentVerifierBuilderProvider
try {
ops.init(verifierProvider, publicKey)
} catch (e: PGPException) {
throw RuntimeException(e)
}
}
}
}
companion object {
@JvmStatic
private val LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream::class.java)
@JvmStatic
fun create(inputStream: InputStream, options: ConsumerOptions) =
create(inputStream, options, PGPainless.getPolicy())
@JvmStatic
fun create(inputStream: InputStream, options: ConsumerOptions, policy: Policy) =
create(inputStream, options, Message(), policy)
@JvmStatic
internal fun create(
inputStream: InputStream,
options: ConsumerOptions,
metadata: Layer,
policy: Policy
): OpenPgpMessageInputStream {
val openPgpIn = OpenPgpInputStream(inputStream)
openPgpIn.reset()
if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) {
return OpenPgpMessageInputStream(
Type.non_openpgp, openPgpIn, options, metadata, policy)
}
if (openPgpIn.isBinaryOpenPgp) {
// Simply consume OpenPGP message
return OpenPgpMessageInputStream(
Type.standard, openPgpIn, options, metadata, policy)
}
return if (openPgpIn.isAsciiArmored) {
val armorIn = ArmoredInputStreamFactory.get(openPgpIn)
if (armorIn.isClearText) {
(metadata as Message).setCleartextSigned()
OpenPgpMessageInputStream(
Type.cleartext_signed, armorIn, options, metadata, policy)
} else {
// Simply consume dearmored OpenPGP message
OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, policy)
}
} else {
throw AssertionError("Cannot deduce type of data.")
}
}
}
}