// SPDX-FileCopyrightText: 2023 Paul Schaub // // 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.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.bouncycastle.extensions.getPublicKeyFor import org.pgpainless.bouncycastle.extensions.getSecretKeyFor import org.pgpainless.bouncycastle.extensions.issuerKeyId import org.pgpainless.bouncycastle.extensions.unlock 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>() // 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 = 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> { val algorithm = pkesk.algorithm val candidates = mutableListOf>() 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 val pkesks: List val anonPkesks: List 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 get() = skesks.plus(pkesks).plus(anonPkesks) } private class Signatures(val options: ConsumerOptions) : OutputStream() { val detachedSignatures = mutableListOf() val prependedSignatures = mutableListOf() val onePassSignatures = mutableListOf() val opsUpdateStack = ArrayDeque>() var literalOPS = mutableListOf() val correspondingSignatures = mutableListOf() val prependedSignaturesWithMissingCert = mutableListOf() val inbandSignaturesWithMissingCert = mutableListOf() val detachedSignaturesWithMissingCert = mutableListOf() var isLiteral = true fun addDetachedSignatures(signatures: Collection) { 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.") } } } }