96 lines
3.7 KiB
Kotlin
96 lines
3.7 KiB
Kotlin
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package org.pgpainless.sop
|
|
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
import java.lang.RuntimeException
|
|
import org.bouncycastle.openpgp.PGPException
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
|
|
import org.pgpainless.PGPainless
|
|
import org.pgpainless.bouncycastle.extensions.openPgpFingerprint
|
|
import org.pgpainless.exception.WrongPassphraseException
|
|
import org.pgpainless.key.util.KeyRingUtils
|
|
import org.pgpainless.key.util.RevocationAttributes
|
|
import org.pgpainless.util.ArmoredOutputStreamFactory
|
|
import org.pgpainless.util.Passphrase
|
|
import sop.Ready
|
|
import sop.exception.SOPGPException
|
|
import sop.operation.RevokeKey
|
|
import sop.util.UTF8Util
|
|
|
|
class RevokeKeyImpl : RevokeKey {
|
|
|
|
private val protector = MatchMakingSecretKeyRingProtector()
|
|
private var armor = true
|
|
|
|
override fun keys(keys: InputStream): Ready {
|
|
val secretKeyRings =
|
|
try {
|
|
KeyReader.readSecretKeys(keys, true)
|
|
} catch (e: IOException) {
|
|
throw SOPGPException.BadData("Cannot decode secret keys.", e)
|
|
}
|
|
|
|
secretKeyRings.forEach { protector.addSecretKey(it) }
|
|
|
|
val revocationCertificates = mutableListOf<PGPPublicKeyRing>()
|
|
secretKeyRings.forEach { secretKeys ->
|
|
val editor = PGPainless.modifyKeyRing(secretKeys)
|
|
try {
|
|
val attributes =
|
|
RevocationAttributes.createKeyRevocation()
|
|
.withReason(RevocationAttributes.Reason.NO_REASON)
|
|
.withoutDescription()
|
|
if (secretKeys.publicKey.version == 6) {
|
|
revocationCertificates.add(
|
|
editor.createMinimalRevocationCertificate(protector, attributes))
|
|
} else {
|
|
val certificate = PGPainless.extractCertificate(secretKeys)
|
|
val revocation = editor.createRevocation(protector, attributes)
|
|
revocationCertificates.add(
|
|
KeyRingUtils.injectCertification(certificate, revocation))
|
|
}
|
|
} catch (e: WrongPassphraseException) {
|
|
throw SOPGPException.KeyIsProtected(
|
|
"Missing or wrong passphrase for key ${secretKeys.openPgpFingerprint}", e)
|
|
} catch (e: PGPException) {
|
|
throw RuntimeException(
|
|
"Cannot generate revocation certificate for key ${secretKeys.openPgpFingerprint}",
|
|
e)
|
|
}
|
|
}
|
|
|
|
return object : Ready() {
|
|
override fun writeTo(outputStream: OutputStream) {
|
|
val collection = PGPPublicKeyRingCollection(revocationCertificates)
|
|
if (armor) {
|
|
val armorOut = ArmoredOutputStreamFactory.get(outputStream)
|
|
collection.encode(armorOut)
|
|
armorOut.close()
|
|
} else {
|
|
collection.encode(outputStream)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun noArmor(): RevokeKey = apply { armor = false }
|
|
|
|
override fun withKeyPassword(password: ByteArray): RevokeKey = apply {
|
|
val string =
|
|
try {
|
|
UTF8Util.decodeUTF8(password)
|
|
} catch (e: CharacterCodingException) {
|
|
// TODO: Add cause
|
|
throw SOPGPException.PasswordNotHumanReadable(
|
|
"Cannot UTF8-decode password: ${e.stackTraceToString()}")
|
|
}
|
|
protector.addPassphrase(Passphrase.fromPassword(string))
|
|
}
|
|
}
|