2023-08-25 13:37:23 +02:00
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification
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.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.protection.UnlockSecretKey
import org.pgpainless.key.util.KeyIdUtil
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.policy.Policy
import org.pgpainless.signature.SignatureUtils
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
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
class OpenPgpMessageInputStream (
type : Type ,
inputStream : InputStream ,
private val options : ConsumerOptions ,
2023-08-30 14:12:05 +02:00
private val layerMetadata : Layer ,
2023-08-25 13:37:23 +02:00
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
2023-08-30 14:12:05 +02:00
if ( layerMetadata is Message ) {
2023-08-30 13:50:49 +02:00
signatures . addDetachedSignatures ( options . getDetachedSignatures ( ) )
2023-08-25 13:37:23 +02:00
}
when ( type ) {
Type . standard -> {
// tee out packet bytes for signature verification
packetInputStream = TeeBCPGInputStream ( BCPGInputStream . wrap ( inputStream ) , signatures )
// *omnomnom*
consumePackets ( )
}
Type . cleartext _signed -> {
2023-08-30 13:50:49 +02:00
val multiPassStrategy = options . getMultiPassStrategy ( )
2023-08-25 13:37:23 +02:00
val detachedSignatures = ClearsignedMessageUtil . detachSignaturesFromInbandClearsignedMessage (
inputStream , multiPassStrategy . messageOutputStream )
for ( signature in detachedSignatures ) {
signatures . addDetachedSignature ( signature )
}
2023-08-30 13:50:49 +02:00
options . isForceNonOpenPgpData ( )
2023-08-25 13:37:23 +02:00
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 ( ) {
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Literal Data Packet at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
syntaxVerifier . next ( InputSymbol . LITERAL _DATA )
val literalData = packetInputStream !! . readLiteralData ( )
// Extract Metadata
2023-08-30 14:12:05 +02:00
layerMetadata . setChild ( LiteralData (
2023-08-25 13:37:23 +02:00
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 ) ,
2023-08-30 14:12:05 +02:00
layerMetadata . depth + 1 )
2023-08-25 13:37:23 +02:00
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Compressed Data Packet ( ${compressionLayer.algorithm} ) at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
nestedInputStream = OpenPgpMessageInputStream ( compressedData . dataStream , options , compressionLayer , policy )
}
private fun processOnePassSignature ( ) {
syntaxVerifier . next ( InputSymbol . ONE _PASS _SIGNATURE )
val ops = packetInputStream !! . readOnePassSignature ( )
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " One-Pass-Signature Packet by key ${KeyIdUtil.formatKeyId(ops.keyID)} at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
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 ) {
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Unsupported Signature at depth ${layerMetadata.depth} encountered. " , e )
2023-08-25 13:37:23 +02:00
return
}
val keyId = SignatureUtils . determineIssuerKeyId ( signature )
if ( isSigForOps ) {
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Signature Packet corresponding to One-Pass-Signature by key ${KeyIdUtil.formatKeyId(keyId)} at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
signatures . leaveNesting ( ) // TODO: Only leave nesting if all OPSs of the nesting layer are dealt with
2023-08-30 14:12:05 +02:00
signatures . addCorrespondingOnePassSignature ( signature , layerMetadata , policy )
2023-08-25 13:37:23 +02:00
} else {
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Prepended Signature Packet by key ${KeyIdUtil.formatKeyId(keyId)} at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
signatures . addPrependedSignature ( signature )
}
}
private fun processEncryptedData ( ) : Boolean {
2023-08-30 14:12:05 +02:00
LOGGER . debug ( " Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered. " )
2023-08-25 13:37:23 +02:00
syntaxVerifier . next ( InputSymbol . ENCRYPTED _DATA )
val encDataList = packetInputStream !! . readEncryptedDataList ( )
if ( ! encDataList . isIntegrityProtected ) {
LOGGER . warn ( " Symmetrically Encrypted Data Packet is not integrity-protected. " )
2023-08-30 13:50:49 +02:00
if ( ! options . isIgnoreMDCErrors ( ) ) {
2023-08-25 13:37:23 +02:00
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
2023-08-30 13:50:49 +02:00
for ( ( key , decryptorFactory ) in options . getCustomDecryptorFactories ( ) ) {
2023-08-25 13:37:23 +02:00
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
2023-08-30 13:50:49 +02:00
if ( options . getSessionKey ( ) != null ) {
val sk = options . getSessionKey ( ) !!
2023-08-25 13:37:23 +02:00
LOGGER . debug ( " Attempt decryption with provided session key. " )
throwIfUnacceptable ( sk . algorithm )
val decryptorFactory = ImplementationFactory . getInstance ( )
. getSessionKeyDataDecryptorFactory ( sk )
2023-08-30 14:12:05 +02:00
val layer = EncryptedData ( sk . algorithm , layerMetadata . depth + 1 )
2023-08-25 13:37:23 +02:00
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
2023-08-30 13:50:49 +02:00
for ( passphrase in options . getDecryptionPassphrases ( ) ) {
2023-08-25 13:37:23 +02:00
for ( skesk in esks . skesks ) {
LOGGER . debug ( " Attempt decryption with provided passphrase " )
val algorithm = SymmetricKeyAlgorithm . requireFromId ( skesk . algorithm )
if ( !is Acceptable ( 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
for ( pkesk in esks . pkesks ) {
val keyId = pkesk . keyID
LOGGER . debug ( " Encountered PKESK for recipient ${KeyIdUtil.formatKeyId(keyId)} " )
val decryptionKeys = getDecryptionKey ( keyId )
if ( decryptionKeys == null ) {
LOGGER . debug ( " Skipping PKESK because no matching key ${KeyIdUtil.formatKeyId(keyId)} was provided. " )
continue
}
val secretKey = decryptionKeys . getSecretKey ( keyId )
val decryptionKeyId = SubkeyIdentifier ( decryptionKeys , keyId )
if ( hasUnsupportedS2KSpecifier ( secretKey , decryptionKeyId ) ) {
continue
}
LOGGER . debug ( " Attempt decryption using secret key $decryptionKeyId " )
2023-08-30 13:50:49 +02:00
val protector = options . getSecretKeyProtector ( decryptionKeys ) ?: continue
2023-08-25 13:37:23 +02:00
if ( ! protector . hasPassphraseFor ( keyId ) ) {
LOGGER . debug ( " Missing passphrase for key $decryptionKeyId . Postponing decryption until all other keys were tried. " )
postponedDueToMissingPassphrase . add ( secretKey to pkesk )
continue
}
val privateKey = UnlockSecretKey . unlockSecretKey ( secretKey , 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 . " )
2023-08-30 13:50:49 +02:00
val protector = options . getSecretKeyProtector ( decryptionKeys ) ?: continue
2023-08-25 13:37:23 +02:00
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 = UnlockSecretKey . unlockSecretKey ( secretKey , protector )
if ( decryptWithPrivateKey ( esks , privateKey , decryptionKeyId , pkesk ) ) {
return true
}
}
}
2023-08-30 13:50:49 +02:00
if ( options . getMissingKeyPassphraseStrategy ( ) == MissingKeyPassphraseStrategy . THROW _EXCEPTION ) {
2023-08-25 13:37:23 +02:00
// 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 ( ) )
}
2023-08-30 13:50:49 +02:00
} else if ( options . getMissingKeyPassphraseStrategy ( ) == MissingKeyPassphraseStrategy . INTERACTIVE ) {
2023-08-25 13:37:23 +02:00
for ( ( secretKey , pkesk ) in postponedDueToMissingPassphrase ) {
val keyId = secretKey . keyID
val decryptionKeys = getDecryptionKey ( keyId ) !!
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 )
val privateKey = UnlockSecretKey . unlockSecretKey ( secretKey , 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 )
2023-08-30 14:12:05 +02:00
val encryptedData = EncryptedData ( sessionKey . algorithm , layerMetadata . depth + 1 )
2023-08-25 13:37:23 +02:00
encryptedData . sessionKey = sessionKey
encryptedData . recipients = 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 ) ) ,
2023-08-30 14:12:05 +02:00
layerMetadata . depth + 1 )
2023-08-25 13:37:23 +02:00
encryptedData . decryptionKey = decryptionKeyId
encryptedData . sessionKey = sessionKey
encryptedData . recipients = 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 . 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 )
}
}
2023-08-30 14:12:05 +02:00
signatures . finish ( layerMetadata , policy )
2023-08-25 13:37:23 +02:00
}
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 )
}
}
2023-08-30 14:12:05 +02:00
signatures . finish ( layerMetadata , policy )
2023-08-25 13:37:23 +02:00
}
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
2023-08-30 14:12:05 +02:00
layerMetadata . setChild ( child . layerMetadata as Nested )
2023-08-25 13:37:23 +02:00
}
}
2023-08-30 14:12:05 +02:00
override val metadata : MessageMetadata
get ( ) {
check ( closed ) { " Stream must be closed before access to metadata can be granted. " }
2023-08-25 13:37:23 +02:00
2023-08-30 14:12:05 +02:00
return MessageMetadata ( ( layerMetadata as Message ) )
}
2023-08-25 13:37:23 +02:00
2023-08-30 13:50:49 +02:00
private fun getDecryptionKey ( keyId : Long ) : PGPSecretKeyRing ? = options . getDecryptionKeys ( ) . firstOrNull {
2023-08-25 13:37:23 +02:00
it . any {
k -> k . keyID == keyId
} . and ( PGPainless . inspectKeyRing ( it ) . decryptionSubkeys . any {
k -> k . keyID == keyId
} )
}
private fun findPotentialDecryptionKeys ( pkesk : PGPPublicKeyEncryptedData ) : List < Pair < PGPSecretKeyRing , PGPSecretKey > > {
val algorithm = pkesk . algorithm
val candidates = mutableListOf < Pair < PGPSecretKeyRing , PGPSecretKey > > ( )
2023-08-30 13:50:49 +02:00
options . getDecryptionKeys ( ) . forEach {
2023-08-25 13:37:23 +02:00
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 ( !is Acceptable ( 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 = SignatureUtils . determineIssuerKeyId ( signature )
if ( check != null ) {
detachedSignatures . add ( check )
} else {
LOGGER . debug ( " No suitable certificate for verification of signature by key ${KeyIdUtil.formatKeyId(keyId)} found. " )
detachedSignaturesWithMissingCert . add ( SignatureVerification . Failure (
SignatureVerification ( signature , null ) ,
SignatureValidationException ( " Missing verification key. " ) ) )
}
}
fun addPrependedSignature ( signature : PGPSignature ) {
val check = initializeSignature ( signature )
val keyId = SignatureUtils . determineIssuerKeyId ( signature )
if ( check != null ) {
prependedSignatures . add ( check )
} else {
LOGGER . debug ( " No suitable certificate for verification of signature by key ${KeyIdUtil.formatKeyId(keyId)} found. " )
prependedSignaturesWithMissingCert . add ( SignatureVerification . Failure (
SignatureVerification ( signature , null ) ,
SignatureValidationException ( " Missing verification key " )
) )
}
}
fun initializeSignature ( signature : PGPSignature ) : SignatureCheck ? {
val keyId = SignatureUtils . determineIssuerKeyId ( signature )
val certificate = findCertificate ( keyId ) ?: return null
val verifierKey = SubkeyIdentifier ( certificate , keyId )
initialize ( signature , certificate , keyId )
return SignatureCheck ( signature , certificate , verifierKey )
}
fun addOnePassSignature ( signature : PGPOnePassSignature ) {
val certificate = findCertificate ( signature . keyID )
if ( certificate != null ) {
val ops = OnePassSignatureCheck ( signature , certificate )
initialize ( signature , certificate )
onePassSignatures . add ( ops )
literalOPS . add ( ops )
}
if ( signature . isContaining ) {
enterNesting ( )
}
}
fun addCorrespondingOnePassSignature ( signature : PGPSignature , layer : Layer , policy : Policy ) {
var found = false
val keyId = SignatureUtils . determineIssuerKeyId ( signature )
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 {
2023-08-30 13:50:49 +02:00
SignatureValidator . signatureWasCreatedInBounds ( options . getVerifyNotBefore ( ) , options . getVerifyNotAfter ( ) )
2023-08-25 13:37:23 +02:00
. 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 ${KeyIdUtil.formatKeyId(keyId)} found. " )
inbandSignaturesWithMissingCert . add ( SignatureVerification . Failure (
SignatureVerification ( signature , null ) ,
SignatureValidationException ( " Missing verification key. " )
) )
}
}
fun enterNesting ( ) {
opsUpdateStack . addFirst ( literalOPS )
literalOPS = mutableListOf ( )
}
fun leaveNesting ( ) {
if ( opsUpdateStack . isEmpty ( ) ) {
return
}
opsUpdateStack . removeFirst ( )
}
fun findCertificate ( keyId : Long ) : PGPPublicKeyRing ? {
2023-08-30 13:50:49 +02:00
val cert = options . getCertificateSource ( ) . getCertificate ( keyId )
2023-08-25 13:37:23 +02:00
if ( cert != null ) {
return cert
}
2023-08-30 13:50:49 +02:00
if ( options . getMissingCertificateCallback ( ) != null ) {
return options . getMissingCertificateCallback ( ) !! . onMissingPublicKeyEncountered ( keyId )
2023-08-25 13:37:23 +02:00
}
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 {
2023-08-30 13:50:49 +02:00
SignatureValidator . signatureWasCreatedInBounds ( options . getVerifyNotBefore ( ) , options . getVerifyNotAfter ( ) )
2023-08-25 13:37:23 +02:00
. 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 {
2023-08-30 13:50:49 +02:00
SignatureValidator . signatureWasCreatedInBounds ( options . getVerifyNotBefore ( ) , options . getVerifyNotAfter ( ) )
2023-08-25 13:37:23 +02:00
. 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 , certificate : PGPPublicKeyRing , keyId : Long ) {
val verifierProvider = ImplementationFactory . getInstance ( )
. pgpContentVerifierBuilderProvider
try {
signature . init ( verifierProvider , certificate . getPublicKey ( keyId ) )
} catch ( e : PGPException ) {
throw RuntimeException ( e )
}
}
@JvmStatic
private fun initialize ( ops : PGPOnePassSignature , certificate : PGPPublicKeyRing ) {
val verifierProvider = ImplementationFactory . getInstance ( )
. pgpContentVerifierBuilderProvider
try {
ops . init ( verifierProvider , certificate . getPublicKey ( ops . keyID ) )
} 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 ( )
2023-08-30 13:50:49 +02:00
if ( openPgpIn . isNonOpenPgp || options . isForceNonOpenPgpData ( ) ) {
2023-08-25 13:37:23 +02:00
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 ) . cleartextSigned = true
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. " )
}
}
}
}