Reimplement PGPainless encryption logic (#1955)
* crypto-pgpainless: reimplement encryption logic * crypto-pgpainless: add an explicit error type for empty keyset
This commit is contained in:
parent
2ba3a08622
commit
d65fc88a14
2 changed files with 24 additions and 12 deletions
|
@ -37,5 +37,8 @@ public sealed class CryptoHandlerException(message: String? = null, cause: Throw
|
||||||
/** The passphrase provided for decryption was incorrect. */
|
/** The passphrase provided for decryption was incorrect. */
|
||||||
public class IncorrectPassphraseException(cause: Throwable) : CryptoHandlerException(null, cause)
|
public class IncorrectPassphraseException(cause: Throwable) : CryptoHandlerException(null, cause)
|
||||||
|
|
||||||
|
/** No keys were provided for encryption. */
|
||||||
|
public class NoKeysProvided(message: String?) : CryptoHandlerException(message, null)
|
||||||
|
|
||||||
/** An unexpected error that cannot be mapped to a known type. */
|
/** An unexpected error that cannot be mapped to a known type. */
|
||||||
public class UnknownError(cause: Throwable) : CryptoHandlerException(null, cause)
|
public class UnknownError(cause: Throwable) : CryptoHandlerException(null, cause)
|
||||||
|
|
|
@ -10,12 +10,12 @@ import com.github.michaelbull.result.mapError
|
||||||
import com.github.michaelbull.result.runCatching
|
import com.github.michaelbull.result.runCatching
|
||||||
import dev.msfjarvis.aps.crypto.errors.CryptoHandlerException
|
import dev.msfjarvis.aps.crypto.errors.CryptoHandlerException
|
||||||
import dev.msfjarvis.aps.crypto.errors.IncorrectPassphraseException
|
import dev.msfjarvis.aps.crypto.errors.IncorrectPassphraseException
|
||||||
|
import dev.msfjarvis.aps.crypto.errors.NoKeysProvided
|
||||||
import dev.msfjarvis.aps.crypto.errors.UnknownError
|
import dev.msfjarvis.aps.crypto.errors.UnknownError
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions
|
import org.pgpainless.decryption_verification.ConsumerOptions
|
||||||
|
@ -64,24 +64,33 @@ public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKe
|
||||||
outputStream: OutputStream,
|
outputStream: OutputStream,
|
||||||
): Result<Unit, CryptoHandlerException> =
|
): Result<Unit, CryptoHandlerException> =
|
||||||
runCatching {
|
runCatching {
|
||||||
|
if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption")
|
||||||
val armoredKeys = keys.map { key -> key.contents.decodeToString() }
|
val armoredKeys = keys.map { key -> key.contents.decodeToString() }
|
||||||
val pubKeysStream = ByteArrayInputStream(armoredKeys.joinToString("\n").toByteArray())
|
val pubKeysStream = ByteArrayInputStream(armoredKeys.joinToString("\n").toByteArray())
|
||||||
val publicKeyRingCollection =
|
val publicKeyRingCollection =
|
||||||
pubKeysStream.use {
|
pubKeysStream.use { PGPainless.readKeyRing().publicKeyRingCollection(pubKeysStream) }
|
||||||
ArmoredInputStream(it).use { armoredInputStream ->
|
val encryptionOptions =
|
||||||
PGPainless.readKeyRing().publicKeyRingCollection(armoredInputStream)
|
EncryptionOptions.encryptCommunications()
|
||||||
}
|
.addRecipients(publicKeyRingCollection.asIterable())
|
||||||
|
val producerOptions = ProducerOptions.encrypt(encryptionOptions).setAsciiArmor(true)
|
||||||
|
val encryptor =
|
||||||
|
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(producerOptions)
|
||||||
|
plaintextStream.copyTo(encryptor)
|
||||||
|
encryptor.close()
|
||||||
|
val result = encryptor.result
|
||||||
|
publicKeyRingCollection.keyRings.forEach { keyRing ->
|
||||||
|
require(result.isEncryptedFor(keyRing)) {
|
||||||
|
"Stream should be encrypted for ${keyRing.publicKey.keyID} but wasn't"
|
||||||
}
|
}
|
||||||
val encOpt =
|
|
||||||
EncryptionOptions().apply { publicKeyRingCollection.forEach { addRecipient(it) } }
|
|
||||||
val prodOpt = ProducerOptions.encrypt(encOpt).setAsciiArmor(true)
|
|
||||||
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(prodOpt).use {
|
|
||||||
encryptionStream ->
|
|
||||||
plaintextStream.copyTo(encryptionStream)
|
|
||||||
}
|
}
|
||||||
return@runCatching
|
return@runCatching
|
||||||
}
|
}
|
||||||
.mapError { error -> UnknownError(error) }
|
.mapError { error ->
|
||||||
|
when (error) {
|
||||||
|
is CryptoHandlerException -> error
|
||||||
|
else -> UnknownError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override fun canHandle(fileName: String): Boolean {
|
public override fun canHandle(fileName: String): Boolean {
|
||||||
return fileName.split('.').lastOrNull() == "gpg"
|
return fileName.split('.').lastOrNull() == "gpg"
|
||||||
|
|
Loading…
Reference in a new issue