From 1a8c29e1ea6b20b0d755a141d3b8bfbb7751724e Mon Sep 17 00:00:00 2001 From: Paul Schaub <vanitasvitae@fsfe.org> Date: Mon, 17 Feb 2025 15:18:58 +0100 Subject: [PATCH] WIP: Port SecretKeyRingEditor over to OpenPGPKeyEditor --- .../main/kotlin/org/pgpainless/PGPainless.kt | 3 + .../secretkeyring/SecretKeyRingEditor.kt | 332 +++++++----------- .../SecretKeyRingEditorInterface.kt | 291 ++++++--------- .../key/info/UserIdRevocationTest.java | 2 +- .../RefuseToAddWeakSubkeyTest.java | 4 +- .../key/modification/RevokeSubKeyTest.java | 6 +- 6 files changed, 242 insertions(+), 396 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index f9e009cf..84d860e6 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -54,6 +54,9 @@ class PGPainless( fun toCertificate(publicKeyRing: PGPPublicKeyRing): OpenPGPCertificate = OpenPGPCertificate(publicKeyRing, implementation) + fun modifyKey(key: OpenPGPKey, referenceTime: Date = Date()): SecretKeyRingEditor = + SecretKeyRingEditor(key, referenceTime) + companion object { @Volatile private var instance: PGPainless? = null diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 2af3724a..6676a6d2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -12,10 +12,13 @@ import openpgp.openPgpKeyId import org.bouncycastle.bcpg.KeyIdentifier import org.bouncycastle.bcpg.sig.KeyExpirationTime import org.bouncycastle.openpgp.* +import org.bouncycastle.openpgp.api.OpenPGPKey +import org.bouncycastle.openpgp.api.OpenPGPKeyEditor +import org.bouncycastle.openpgp.api.SignatureParameters +import org.bouncycastle.openpgp.api.SignatureParameters.Callback +import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction import org.pgpainless.PGPainless import org.pgpainless.PGPainless.Companion.inspectKeyRing -import org.pgpainless.algorithm.AlgorithmSuite -import org.pgpainless.algorithm.Feature import org.pgpainless.algorithm.KeyFlag import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.algorithm.SignatureType @@ -23,8 +26,8 @@ import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator import org.pgpainless.bouncycastle.extensions.getKeyExpirationDate import org.pgpainless.bouncycastle.extensions.publicKeyAlgorithm import org.pgpainless.bouncycastle.extensions.requirePublicKey +import org.pgpainless.bouncycastle.extensions.toOpenPGPKey import org.pgpainless.implementation.ImplementationFactory -import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.generation.KeyRingBuilder import org.pgpainless.key.generation.KeySpec import org.pgpainless.key.protection.* @@ -38,66 +41,38 @@ import org.pgpainless.signature.subpackets.* import org.pgpainless.util.Passphrase import org.pgpainless.util.selection.userid.SelectUserId -class SecretKeyRingEditor( - var secretKeyRing: PGPSecretKeyRing, - override val referenceTime: Date = Date() +class SecretKeyRingEditor(key: OpenPGPKey, + keyProtector: SecretKeyRingProtector, + override val referenceTime: Date = Date() ) : SecretKeyRingEditorInterface { + private var editor: OpenPGPKeyEditor = OpenPGPKeyEditor(key, keyProtector, PGPainless.getInstance().implementation) + + constructor( + secretKey: PGPSecretKeyRing, + referenceTime: Date = Date(), + keyProtector: SecretKeyRingProtector + ) : this(secretKey.toOpenPGPKey(), keyProtector, referenceTime) + override fun addUserId( userId: CharSequence, - callback: SelfSignatureSubpackets.Callback?, - protector: SecretKeyRingProtector + callback: SelfSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { val sanitizedUserId = sanitizeUserId(userId).toString() - val primaryKey = secretKeyRing.secretKey - - val info = inspectKeyRing(secretKeyRing, referenceTime) - require(!info.isHardRevoked(userId)) { - "User-ID $userId is hard revoked and cannot be re-certified." - } - - val ( - hashAlgorithmPreferences, - symmetricKeyAlgorithmPreferences, - compressionAlgorithmPreferences) = - try { - Triple( - info.preferredHashAlgorithms, - info.preferredSymmetricKeyAlgorithms, - info.preferredCompressionAlgorithms) - } catch (e: IllegalStateException) { // missing user-id sig - val algorithmSuite = AlgorithmSuite.defaultAlgorithmSuite - Triple( - algorithmSuite.hashAlgorithms, - algorithmSuite.symmetricKeyAlgorithms, - algorithmSuite.compressionAlgorithms) + editor.addUserId(sanitizedUserId, object : Callback { + override fun apply(parameters: SignatureParameters): SignatureParameters { + return parameters.setSignatureCreationTime(referenceTime) } - - val builder = - SelfSignatureBuilder(primaryKey, protector).apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - setSignatureType(SignatureType.POSITIVE_CERTIFICATION) - } - builder.hashedSubpackets.apply { - setKeyFlags(info.getKeyFlagsOf(primaryKey.keyID)) - setPreferredHashAlgorithms(hashAlgorithmPreferences) - setPreferredSymmetricKeyAlgorithms(symmetricKeyAlgorithmPreferences) - setPreferredCompressionAlgorithms(compressionAlgorithmPreferences) - setFeatures(Feature.MODIFICATION_DETECTION) - } - builder.applyCallback(callback) - secretKeyRing = - injectCertification(secretKeyRing, sanitizedUserId, builder.build(sanitizedUserId)) + }) return this } override fun addPrimaryUserId( userId: CharSequence, - protector: SecretKeyRingProtector ): SecretKeyRingEditorInterface { val uid = sanitizeUserId(userId) - val primaryKey = secretKeyRing.publicKey - var info = inspectKeyRing(secretKeyRing, referenceTime) + val primaryKey = key.publicKey + var info = inspectKeyRing(key, referenceTime) val primaryUserId = info.primaryUserId val signature = if (primaryUserId == null) info.latestDirectKeySelfSignature @@ -116,18 +91,17 @@ class SecretKeyRingEditor( else setKeyExpirationTime(null) } } - }, - protector) + }) // unmark previous primary user-ids to be non-primary - info = inspectKeyRing(secretKeyRing, referenceTime) + info = inspectKeyRing(key, referenceTime) info.validAndExpiredUserIds .filterNot { it == uid } .forEach { otherUserId -> if (info - .getLatestUserIdCertification(otherUserId)!! - .hashedSubPackets - .isPrimaryUserID) { + .getLatestUserIdCertification(otherUserId)!! + .hashedSubPackets + .isPrimaryUserID) { // We need to unmark this user-id as primary addUserId( otherUserId, @@ -140,34 +114,16 @@ class SecretKeyRingEditor( setKeyExpirationTime(null) // non-primary } } - }, - protector) + }) } } return this } - @Deprecated( - "Use of SelectUserId class is deprecated.", - replaceWith = ReplaceWith("removeUserId(protector, predicate)")) override fun removeUserId( - selector: SelectUserId, - protector: SecretKeyRingProtector - ): SecretKeyRingEditorInterface { - return revokeUserIds( - selector, - protector, - RevocationAttributes.createCertificateRevocation() - .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) - .withoutDescription()) - } - - override fun removeUserId( - protector: SecretKeyRingProtector, predicate: (String) -> Boolean ): SecretKeyRingEditorInterface { return revokeUserIds( - protector, RevocationAttributes.createCertificateRevocation() .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) .withoutDescription(), @@ -175,23 +131,21 @@ class SecretKeyRingEditor( } override fun removeUserId( - userId: CharSequence, - protector: SecretKeyRingProtector + userId: CharSequence ): SecretKeyRingEditorInterface { - return removeUserId(protector) { uid -> userId == uid } + return removeUserId { uid -> userId == uid } } override fun replaceUserId( oldUserId: CharSequence, - newUserId: CharSequence, - protector: SecretKeyRingProtector + newUserId: CharSequence ): SecretKeyRingEditorInterface { val oldUID = sanitizeUserId(oldUserId) val newUID = sanitizeUserId(newUserId) require(oldUID.isNotBlank()) { "Old user-ID cannot be empty." } require(newUID.isNotBlank()) { "New user-ID cannot be empty." } - val info = inspectKeyRing(secretKeyRing, referenceTime) + val info = inspectKeyRing(key, referenceTime) if (!info.isUserIdValid(oldUID)) { throw NoSuchElementException( "Key does not carry user-ID '$oldUID', or it is not valid.") @@ -218,16 +172,14 @@ class SecretKeyRingEditor( oldCertification.unhashedSubPackets, unhashedSubpackets as SignatureSubpackets) } - }, - protector) + }) - return revokeUserId(oldUID, protector) + return revokeUserId(oldUID) } - override fun addSubKey( + override fun addSubkey( keySpec: KeySpec, - subkeyPassphrase: Passphrase, - protector: SecretKeyRingProtector + subkeyPassphrase: Passphrase ): SecretKeyRingEditorInterface { val callback = object : SelfSignatureSubpackets.Callback { @@ -237,34 +189,31 @@ class SecretKeyRingEditor( hashedSubpackets.setSignatureCreationTime(referenceTime) } } - return addSubKey(keySpec, subkeyPassphrase, callback, protector) + return addSubkey(keySpec, subkeyPassphrase, callback) } - override fun addSubKey( + override fun addSubkey( keySpec: KeySpec, subkeyPassphrase: Passphrase, - callback: SelfSignatureSubpackets.Callback?, - protector: SecretKeyRingProtector + callback: SelfSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { - val version = OpenPGPKeyVersion.from(secretKeyRing.getPublicKey().version) + val version = OpenPGPKeyVersion.from(key.getPublicKey().version) val keyPair = KeyRingBuilder.generateKeyPair(keySpec, OpenPGPKeyVersion.v4, referenceTime) val subkeyProtector = PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyIdentifier, subkeyPassphrase) val keyFlags = KeyFlag.fromBitmask(keySpec.subpackets.keyFlags).toMutableList() - return addSubKey( + return addSubkey( keyPair, callback, subkeyProtector, - protector, keyFlags.removeFirst(), *keyFlags.toTypedArray()) } - override fun addSubKey( + override fun addSubkey( subkey: PGPKeyPair, callback: SelfSignatureSubpackets.Callback?, subkeyProtector: SecretKeyRingProtector, - primaryKeyProtector: SecretKeyRingProtector, keyFlag: KeyFlag, vararg keyFlags: KeyFlag ): SecretKeyRingEditorInterface { @@ -277,11 +226,11 @@ class SecretKeyRingEditor( PGPainless.getPolicy() .publicKeyAlgorithmPolicy .isAcceptable(subkeyAlgorithm, bitStrength)) { - "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable." - } + "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable." + } - val primaryKey = secretKeyRing.secretKey - val info = inspectKeyRing(secretKeyRing, referenceTime) + val primaryKey = key.secretKey + val info = inspectKeyRing(key, referenceTime) val hashAlgorithm = HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) .negotiateHashAlgorithm(info.preferredHashAlgorithms) @@ -309,61 +258,60 @@ class SecretKeyRingEditor( secretSubkey = KeyRingUtils.secretKeyPlusSignature( secretSubkey, skBindingBuilder.build(secretSubkey.publicKey)) - secretKeyRing = KeyRingUtils.keysPlusSecretKey(secretKeyRing, secretSubkey) + key = KeyRingUtils.keysPlusSecretKey(key, secretSubkey) return this } override fun revoke( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? ): SecretKeyRingEditorInterface { - return revoke(protector, callbackFromRevocationAttributes(revocationAttributes)) + return revoke(callbackFromRevocationAttributes(revocationAttributes)) } override fun revoke( - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { - return revokeSubKey(secretKeyRing.secretKey.keyID, protector, callback) + editor.revokeKey(object : SignatureParameters.Callback { + override fun apply(parameters: SignatureParameters?): SignatureParameters { + if (callback != null) { + callback.modifyHashedSubpackets() + } + } + }) } - override fun revokeSubKey( - subkeyId: Long, - protector: SecretKeyRingProtector, + override fun revokeSubkey( + subkeyIdentifier: KeyIdentifier, revocationAttributes: RevocationAttributes? ): SecretKeyRingEditorInterface { - return revokeSubKey( - subkeyId, protector, callbackFromRevocationAttributes(revocationAttributes)) + return revokeSubkey(subkeyIdentifier, callbackFromRevocationAttributes(revocationAttributes)) } - override fun revokeSubKey( + override fun revokeSubkey( subkeyId: Long, - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { - val revokeeSubKey = secretKeyRing.requirePublicKey(subkeyId) - val subkeyRevocation = generateRevocation(protector, revokeeSubKey, callback) - secretKeyRing = injectCertification(secretKeyRing, revokeeSubKey, subkeyRevocation) + val revokeeSubKey = key.requirePublicKey(subkeyId) + val subkeyRevocation = generateRevocation(revokeeSubKey, callback) + key = injectCertification(key, revokeeSubKey, subkeyRevocation) return this } override fun revokeUserId( userId: CharSequence, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? ): SecretKeyRingEditorInterface { if (revocationAttributes != null) { require( revocationAttributes.reason == RevocationAttributes.Reason.NO_REASON || revocationAttributes.reason == - RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) { - "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID" - } + RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) { + "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID" + } } return revokeUserId( userId, - protector, object : RevocationSignatureSubpackets.Callback { override fun modifyHashedSubpackets( hashedSubpackets: RevocationSignatureSubpackets @@ -377,19 +325,16 @@ class SecretKeyRingEditor( override fun revokeUserId( userId: CharSequence, - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { - return revokeUserIds(protector, callback, SelectUserId.exactMatch(sanitizeUserId(userId))) + return revokeUserIds(callback, SelectUserId.exactMatch(sanitizeUserId(userId))) } override fun revokeUserIds( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?, predicate: (String) -> Boolean ): SecretKeyRingEditorInterface { return revokeUserIds( - protector, object : RevocationSignatureSubpackets.Callback { override fun modifyHashedSubpackets( hashedSubpackets: RevocationSignatureSubpackets @@ -402,7 +347,6 @@ class SecretKeyRingEditor( } override fun revokeUserIds( - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?, predicate: (String) -> Boolean ): SecretKeyRingEditorInterface { @@ -411,38 +355,36 @@ class SecretKeyRingEditor( if (it.isEmpty()) throw NoSuchElementException("No matching user-ids found on the key.") } - .forEach { userId -> doRevokeUserId(userId, protector, callback) } + .forEach { userId -> doRevokeUserId(userId, callback) } return this } override fun setExpirationDate( - expiration: Date?, - protector: SecretKeyRingProtector + expiration: Date? ): SecretKeyRingEditorInterface { - require(secretKeyRing.secretKey.isMasterKey) { + require(key.secretKey.isMasterKey) { "OpenPGP key does not appear to contain a primary secret key." } val prevDirectKeySig = getPreviousDirectKeySignature() // reissue direct key sig if (prevDirectKeySig != null) { - secretKeyRing = + key = injectCertification( - secretKeyRing, - secretKeyRing.publicKey, - reissueDirectKeySignature(expiration, protector, prevDirectKeySig)) + key, + key.publicKey, + reissueDirectKeySignature(expiration, prevDirectKeySig)) } - val primaryUserId = - inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId() + val primaryUserId = inspectKeyRing(key, referenceTime).getPossiblyExpiredPrimaryUserId() if (primaryUserId != null) { val prevUserIdSig = getPreviousUserIdSignatures(primaryUserId) val userIdSig = - reissuePrimaryUserIdSig(expiration, protector, primaryUserId, prevUserIdSig!!) - secretKeyRing = injectCertification(secretKeyRing, primaryUserId, userIdSig) + reissuePrimaryUserIdSig(expiration, primaryUserId, prevUserIdSig!!) + key = injectCertification(key, primaryUserId, userIdSig) } - val info = inspectKeyRing(secretKeyRing, referenceTime) + val info = inspectKeyRing(key, referenceTime) for (userId in info.validUserIds) { if (userId == primaryUserId) { continue @@ -453,11 +395,11 @@ class SecretKeyRingEditor( ?: throw AssertionError( "A valid user-id shall never have no user-id signature.") if (prevUserIdSig.hashedSubPackets.isPrimaryUserID) { - secretKeyRing = + key = injectCertification( - secretKeyRing, + key, primaryUserId!!, - reissueNonPrimaryUserId(protector, userId, prevUserIdSig)) + reissueNonPrimaryUserId(userId, prevUserIdSig)) } } @@ -466,28 +408,26 @@ class SecretKeyRingEditor( override fun setExpirationDateOfSubkey( expiration: Date?, - keyId: Long, - protector: SecretKeyRingProtector + keyIdentifier: KeyIdentifier ): SecretKeyRingEditorInterface = apply { // is primary key - if (keyId == secretKeyRing.publicKey.keyID) { - return setExpirationDate(expiration, protector) + if (keyId == key.publicKey.keyID) { + return setExpirationDate(expiration) } // is subkey val subkey = - secretKeyRing.getPublicKey(keyId) + key.getPublicKey(keyId) ?: throw NoSuchElementException("No subkey with ID ${keyId.openPgpKeyId()} found.") val prevBinding = - inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId) + inspectKeyRing(key).getCurrentSubkeyBindingSignature(keyId) ?: throw NoSuchElementException( "Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.") - val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding) - secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig) + val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, prevBinding) + key = injectCertification(key, subkey, bindingSig) } override fun createMinimalRevocationCertificate( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? ): PGPPublicKeyRing { // Check reason @@ -497,51 +437,34 @@ class SecretKeyRingEditor( } } - val revocation = createRevocation(protector, revocationAttributes) - var primaryKey = secretKeyRing.secretKey.publicKey + val revocation = createRevocation(revocationAttributes) + var primaryKey = key.secretKey.publicKey primaryKey = KeyRingUtils.getStrippedDownPublicKey(primaryKey) primaryKey = PGPPublicKey.addCertification(primaryKey, revocation) return PGPPublicKeyRing(listOf(primaryKey)) } override fun createRevocation( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? ): PGPSignature { return generateRevocation( - protector, - secretKeyRing.publicKey, + key.publicKey, callbackFromRevocationAttributes(revocationAttributes)) + } + + override fun createRevocation( + subkeyIdentifier: KeyIdentifier, + revocationAttributes: RevocationAttributes? + ): PGPSignature { + return generateRevocation( + key.requirePublicKey(subkeyId), callbackFromRevocationAttributes(revocationAttributes)) } override fun createRevocation( - subkeyId: Long, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? - ): PGPSignature { - return generateRevocation( - protector, - secretKeyRing.requirePublicKey(subkeyId), - callbackFromRevocationAttributes(revocationAttributes)) - } - - override fun createRevocation( - subkeyId: Long, - protector: SecretKeyRingProtector, + subkeyIdentifier: KeyIdentifier, callback: RevocationSignatureSubpackets.Callback? ): PGPSignature { - return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyId), callback) - } - - override fun createRevocation( - subkeyFingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? - ): PGPSignature { - return generateRevocation( - protector, - secretKeyRing.requirePublicKey(subkeyFingerprint), - callbackFromRevocationAttributes(revocationAttributes)) + return generateRevocation(key.requirePublicKey(subkeyId), callback) } override fun changePassphraseFromOldPassphrase( @@ -567,12 +490,12 @@ class SecretKeyRingEditor( mapOf(keyIdentifier to oldPassphrase), oldProtectionSettings, null)) } - override fun done(): PGPSecretKeyRing { - return secretKeyRing + override fun done(): OpenPGPKey { + return editor.done() } private fun sanitizeUserId(userId: CharSequence): CharSequence = - // TODO: Further research how to sanitize user IDs. + // TODO: Further research how to sanitize user IDs. // e.g. what about newlines? userId.toString().trim() @@ -586,45 +509,39 @@ class SecretKeyRingEditor( } private fun generateRevocation( - protector: SecretKeyRingProtector, revokeeSubkey: PGPPublicKey, callback: RevocationSignatureSubpackets.Callback? ): PGPSignature { - val primaryKey = secretKeyRing.secretKey + val primaryKey = key.secretKey val signatureType = if (revokeeSubkey.isMasterKey) SignatureType.KEY_REVOCATION else SignatureType.SUBKEY_REVOCATION - return RevocationSignatureBuilder(signatureType, primaryKey, protector) + return RevocationSignatureBuilder(signatureType, primaryKey) .apply { applyCallback(callback) } .build(revokeeSubkey) } private fun doRevokeUserId( userId: CharSequence, - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface { - RevocationSignatureBuilder( - SignatureType.CERTIFICATION_REVOCATION, secretKeyRing.secretKey, protector) + RevocationSignatureBuilder(SignatureType.CERTIFICATION_REVOCATION, key.secretKey) .apply { hashedSubpackets.setSignatureCreationTime(referenceTime) applyCallback(callback) } - .let { - secretKeyRing = - injectCertification(secretKeyRing, userId, it.build(userId.toString())) - } + .let { key = injectCertification(key, userId, it.build(userId.toString())) } return this } private fun getPreviousDirectKeySignature(): PGPSignature? { - val info = inspectKeyRing(secretKeyRing, referenceTime) + val info = inspectKeyRing(key, referenceTime) return info.latestDirectKeySelfSignature } private fun getPreviousUserIdSignatures(userId: String): PGPSignature? { - val info = inspectKeyRing(secretKeyRing, referenceTime) + val info = inspectKeyRing(key, referenceTime) return info.getLatestUserIdCertification(userId) } @@ -634,8 +551,7 @@ class SecretKeyRingEditor( userId: String, prevUserIdSig: PGPSignature ): PGPSignature { - val builder = - SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig) + val builder = SelfSignatureBuilder(key.secretKey, secretKeyRingProtector, prevUserIdSig) builder.hashedSubpackets.setSignatureCreationTime(referenceTime) builder.applyCallback( object : SelfSignatureSubpackets.Callback { @@ -654,7 +570,7 @@ class SecretKeyRingEditor( @Nonnull primaryUserId: String, @Nonnull prevUserIdSig: PGPSignature ): PGPSignature { - return SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig) + return SelfSignatureBuilder(key.secretKey, secretKeyRingProtector, prevUserIdSig) .apply { hashedSubpackets.setSignatureCreationTime(referenceTime) applyCallback( @@ -664,7 +580,7 @@ class SecretKeyRingEditor( ) { if (expiration != null) { hashedSubpackets.setKeyExpirationTime( - true, secretKeyRing.publicKey.creationTime, expiration) + true, key.publicKey.creationTime, expiration) } else { hashedSubpackets.setKeyExpirationTime(KeyExpirationTime(true, 0)) } @@ -682,7 +598,7 @@ class SecretKeyRingEditor( prevDirectKeySig: PGPSignature ): PGPSignature { return DirectKeySelfSignatureBuilder( - secretKeyRing.secretKey, secretKeyRingProtector, prevDirectKeySig) + key.secretKey, secretKeyRingProtector, prevDirectKeySig) .apply { hashedSubpackets.setSignatureCreationTime(referenceTime) applyCallback( @@ -692,7 +608,7 @@ class SecretKeyRingEditor( ) { if (expiration != null) { hashedSubpackets.setKeyExpirationTime( - secretKeyRing.publicKey.creationTime, expiration) + key.publicKey.creationTime, expiration) } else { hashedSubpackets.setKeyExpirationTime(null) } @@ -708,9 +624,9 @@ class SecretKeyRingEditor( protector: SecretKeyRingProtector, prevSubkeyBindingSignature: PGPSignature ): PGPSignature { - val primaryKey = secretKeyRing.publicKey - val secretPrimaryKey = secretKeyRing.secretKey - val secretSubkey: PGPSecretKey? = secretKeyRing.getSecretKey(subkey.keyID) + val primaryKey = key.publicKey + val secretPrimaryKey = key.secretKey + val secretSubkey: PGPSecretKey? = key.getSecretKey(subkey.keyID) val builder = SubkeyBindingSignatureBuilder(secretPrimaryKey, protector, prevSubkeyBindingSignature) @@ -742,7 +658,7 @@ class SecretKeyRingEditor( } private fun selectUserIds(predicate: Predicate<String>): List<String> = - inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) } + inspectKeyRing(key).validUserIds.filter { predicate.test(it) } private class WithKeyRingEncryptionSettingsImpl( private val editor: SecretKeyRingEditor, @@ -772,15 +688,15 @@ class SecretKeyRingEditor( val protector = PasswordBasedSecretKeyRingProtector( newProtectionSettings, SolitaryPassphraseProvider(passphrase)) - val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector) - editor.secretKeyRing = secretKeys + val secretKeys = changePassphrase(keyId, editor.key, oldProtector, protector) + editor.key = secretKeys return editor } override fun toNoPassphrase(): SecretKeyRingEditorInterface { val protector = UnprotectedKeysProtector() - val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector) - editor.secretKeyRing = secretKeys + val secretKeys = changePassphrase(keyId, editor.key, oldProtector, protector) + editor.key = secretKeys return editor } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt index ad8e36ff..74e60a36 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt @@ -10,6 +10,7 @@ import java.security.NoSuchAlgorithmException import java.util.* import org.bouncycastle.bcpg.KeyIdentifier import org.bouncycastle.openpgp.* +import org.bouncycastle.openpgp.api.OpenPGPKey import org.pgpainless.algorithm.KeyFlag import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.generation.KeySpec @@ -19,7 +20,6 @@ import org.pgpainless.key.util.RevocationAttributes import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.util.Passphrase -import org.pgpainless.util.selection.userid.SelectUserId interface SecretKeyRingEditorInterface { @@ -33,28 +33,23 @@ interface SecretKeyRingEditorInterface { * Add a user-id to the key ring. * * @param userId user-id - * @param protector protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a signature for the user-id */ - @Throws(PGPException::class) - fun addUserId(userId: CharSequence, protector: SecretKeyRingProtector) = - addUserId(userId, null, protector) + @Throws(PGPException::class) fun addUserId(userId: CharSequence) = addUserId(userId, null) /** * Add a user-id to the key ring. * * @param userId user-id * @param callback callback to modify the self-signature subpackets - * @param protector protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a signature for the user-id */ @Throws(PGPException::class) fun addUserId( userId: CharSequence, - callback: SelfSignatureSubpackets.Callback? = null, - protector: SecretKeyRingProtector + callback: SelfSignatureSubpackets.Callback? = null ): SecretKeyRingEditorInterface /** @@ -62,48 +57,23 @@ interface SecretKeyRingEditorInterface { * new certification signature will be created. * * @param userId user id - * @param protector protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a signature for the user-id */ @Throws(PGPException::class) - fun addPrimaryUserId( - userId: CharSequence, - protector: SecretKeyRingProtector - ): SecretKeyRingEditorInterface + fun addPrimaryUserId(userId: CharSequence): SecretKeyRingEditorInterface /** * Convenience method to revoke selected user-ids using soft revocation signatures. The * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the * user-id can be re-certified at a later point. * - * @param selector selector to select user-ids - * @param protector protector to unlock the primary key - * @return the builder - * @throws PGPException in case we cannot generate a revocation signature for the user-id - */ - @Deprecated( - "Use of SelectUserId class is deprecated.", - ReplaceWith("removeUserId(protector, predicate)")) - @Throws(PGPException::class) - fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector) = - removeUserId(protector, selector) - - /** - * Convenience method to revoke selected user-ids using soft revocation signatures. The - * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the - * user-id can be re-certified at a later point. - * - * @param protector protector to unlock the primary key * @param predicate predicate to select user-ids for revocation * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun removeUserId( - protector: SecretKeyRingProtector, - predicate: (String) -> Boolean - ): SecretKeyRingEditorInterface + fun removeUserId(predicate: (String) -> Boolean): SecretKeyRingEditorInterface /** * Convenience method to revoke a single user-id using a soft revocation signature. The @@ -111,15 +81,11 @@ interface SecretKeyRingEditorInterface { * can be re-certified at a later point. * * @param userId user-id to revoke - * @param protector protector to unlock the primary key * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun removeUserId( - userId: CharSequence, - protector: SecretKeyRingProtector - ): SecretKeyRingEditorInterface + fun removeUserId(userId: CharSequence): SecretKeyRingEditorInterface /** * Replace a user-id on the key with a new one. The old user-id gets soft revoked and the new @@ -130,7 +96,6 @@ interface SecretKeyRingEditorInterface { * * @param oldUserId old user-id * @param newUserId new user-id - * @param protector protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a revocation and certification signature * @throws java.util.NoSuchElementException if the old user-id was not found on the key; or if @@ -139,8 +104,7 @@ interface SecretKeyRingEditorInterface { @Throws(PGPException::class) fun replaceUserId( oldUserId: CharSequence, - newUserId: CharSequence, - protector: SecretKeyRingProtector + newUserId: CharSequence ): SecretKeyRingEditorInterface /** @@ -149,7 +113,6 @@ interface SecretKeyRingEditorInterface { * @param keySpec key specification * @param subkeyPassphrase passphrase to encrypt the sub key * @param callback callback to modify the subpackets of the subkey binding signature - * @param protector protector to unlock the secret key of the key ring * @return the builder * @throws InvalidAlgorithmParameterException in case the user wants to use invalid parameters * for the key @@ -162,11 +125,10 @@ interface SecretKeyRingEditorInterface { IOException::class, InvalidAlgorithmParameterException::class, NoSuchAlgorithmException::class) - fun addSubKey( + fun addSubkey( keySpec: KeySpec, subkeyPassphrase: Passphrase, - callback: SelfSignatureSubpackets.Callback? = null, - protector: SecretKeyRingProtector + callback: SelfSignatureSubpackets.Callback? = null ): SecretKeyRingEditorInterface /** @@ -175,7 +137,6 @@ interface SecretKeyRingEditorInterface { * @param subkey subkey key pair * @param callback callback to modify the subpackets of the subkey binding signature * @param subkeyProtector protector to unlock and encrypt the subkey - * @param primaryKeyProtector protector to unlock the primary key * @param keyFlag first mandatory key flag for the subkey * @param keyFlags optional additional key flags * @return builder @@ -183,11 +144,10 @@ interface SecretKeyRingEditorInterface { * @throws IOException in case of an IO error */ @Throws(PGPException::class, IOException::class) - fun addSubKey( + fun addSubkey( subkey: PGPKeyPair, callback: SelfSignatureSubpackets.Callback?, subkeyProtector: SecretKeyRingProtector, - primaryKeyProtector: SecretKeyRingProtector, keyFlag: KeyFlag, vararg keyFlags: KeyFlag ): SecretKeyRingEditorInterface @@ -195,42 +155,60 @@ interface SecretKeyRingEditorInterface { /** * Revoke the key ring using a hard revocation. * - * @param protector protector of the primary key * @return the builder * @throws PGPException in case we cannot generate a revocation signature */ - @Throws(PGPException::class) - fun revoke(protector: SecretKeyRingProtector) = revoke(protector, null as RevocationAttributes?) + @Throws(PGPException::class) fun revoke() = revoke(null as RevocationAttributes?) /** * Revoke the key ring using the provided revocation attributes. The attributes define, whether * the revocation was a hard revocation or not. * - * @param protector protector of the primary key * @param revocationAttributes reason for the revocation * @return the builder * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) - fun revoke( - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? = null - ): SecretKeyRingEditorInterface + fun revoke(revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface /** * Revoke the key ring. You can use the [RevocationSignatureSubpackets.Callback] to modify the * revocation signatures subpackets, e.g. in order to define whether this is a hard or soft * revocation. * - * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @return builder * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) - fun revoke( - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback? + fun revoke(callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface + + fun revokeSubkey(subkeyIdentifier: KeyIdentifier) = revokeSubkey(subkeyIdentifier, null) + + /** + * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint + * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * + * @param fingerprint fingerprint of the subkey to be revoked + * @return the builder + * @throws PGPException in case we cannot generate a revocation signature for the subkey + */ + @Throws(PGPException::class) + @Deprecated("Pass in the subkeys KeyIdentifier instead.") + fun revokeSubkey(fingerprint: OpenPgpFingerprint) = revokeSubkey(fingerprint.keyIdentifier) + + /** + * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint + * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * + * @param subkeyIdentifier identifier of the subkey to be revoked + * @return the builder + * @throws PGPException in case we cannot generate a revocation signature for the subkey + */ + @Throws(PGPException::class) + fun revokeSubkey( + subkeyIdentifier: KeyIdentifier, + revocationAttributes: RevocationAttributes? = null ): SecretKeyRingEditorInterface /** @@ -238,61 +216,44 @@ interface SecretKeyRingEditorInterface { * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param fingerprint fingerprint of the subkey to be revoked - * @param protector protector to unlock the primary key - * @return the builder - * @throws PGPException in case we cannot generate a revocation signature for the subkey - */ - @Throws(PGPException::class) - fun revokeSubKey(fingerprint: OpenPgpFingerprint, protector: SecretKeyRingProtector) = - revokeSubKey(fingerprint, protector, null) - - /** - * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint - * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. - * - * @param fingerprint fingerprint of the subkey to be revoked - * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey( + @Deprecated("Pass in the subkeys KeyIdentifier instead.") + fun revokeSubkey( fingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? = null - ): SecretKeyRingEditorInterface = - revokeSubKey(fingerprint.keyId, protector, revocationAttributes) + ): SecretKeyRingEditorInterface = revokeSubkey(fingerprint.keyIdentifier, revocationAttributes) /** * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param subkeyId id of the subkey - * @param protector protector to unlock the primary key * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector) = - revokeSubKey(subkeyId, protector, null as RevocationAttributes?) + @Deprecated("Pass in the keys KeyIdentifier instead.") + fun revokeSubkey(subkeyId: Long) = + revokeSubkey(KeyIdentifier(subkeyId), null as RevocationAttributes?) /** * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param subkeyId id of the subkey - * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey( + fun revokeSubkey( subkeyId: Long, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? = null - ): SecretKeyRingEditorInterface + ): SecretKeyRingEditorInterface = revokeSubkey(KeyIdentifier(subkeyId), revocationAttributes) /** * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be @@ -301,16 +262,14 @@ interface SecretKeyRingEditorInterface { * The provided subpackets callback is used to modify the revocation signatures subpackets. * * @param subkeyId id of the subkey - * @param protector protector to unlock the secret key ring * @param callback callback which can be used to modify the subpackets of the revocation * signature * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey( + fun revokeSubkey( subkeyId: Long, - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface @@ -318,19 +277,16 @@ interface SecretKeyRingEditorInterface { * Hard-revoke the given userID. * * @param userId userId to revoke - * @param protector protector to unlock the primary key * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector) = - revokeUserId(userId, protector, null as RevocationAttributes?) + fun revokeUserId(userId: CharSequence) = revokeUserId(userId, null as RevocationAttributes?) /** * Revoke the given userID using the provided revocation attributes. * * @param userId userId to revoke - * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder * @throws PGPException in case we cannot generate a revocation signature for the user-id @@ -338,7 +294,6 @@ interface SecretKeyRingEditorInterface { @Throws(PGPException::class) fun revokeUserId( userId: CharSequence, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? = null ): SecretKeyRingEditorInterface @@ -350,7 +305,6 @@ interface SecretKeyRingEditorInterface { * revocation reason in the signatures hashed area using the subpacket callback. * * @param userId userid to be revoked - * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @return builder * @throws PGPException in case we cannot generate a revocation signature for the user-id @@ -358,41 +312,16 @@ interface SecretKeyRingEditorInterface { @Throws(PGPException::class) fun revokeUserId( userId: CharSequence, - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback? ): SecretKeyRingEditorInterface /** - * Revoke all user-ids that match the provided [SelectUserId] filter. The provided - * [RevocationAttributes] will be set as reason for revocation in each revocation signature. + * Revoke all user-ids that match the provided [predicate]. The provided [RevocationAttributes] + * will be set as reason for revocation in each revocation signature. * * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft * revocation reason. See [RevocationAttributes.Reason] for more information. * - * @param selector user-id selector - * @param protector protector to unlock the primary secret key - * @param revocationAttributes revocation attributes - * @return builder - * @throws PGPException in case we cannot generate a revocation signature for the user-id - */ - @Throws(PGPException::class) - @Deprecated( - "Use of SelectUserId class is deprecated.", - ReplaceWith("revokeUserIds(protector, revocationAttributes, predicate)")) - fun revokeUserIds( - selector: SelectUserId, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? - ) = revokeUserIds(protector, revocationAttributes, selector) - - /** - * Revoke all user-ids that match the provided [SelectUserId] filter. The provided - * [RevocationAttributes] will be set as reason for revocation in each revocation signature. - * - * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft - * revocation reason. See [RevocationAttributes.Reason] for more information. - * - * @param protector protector to unlock the primary secret key * @param revocationAttributes revocation attributes * @param predicate to select user-ids for revocation * @return builder @@ -400,13 +329,12 @@ interface SecretKeyRingEditorInterface { */ @Throws(PGPException::class) fun revokeUserIds( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?, predicate: (String) -> Boolean ): SecretKeyRingEditorInterface /** - * Revoke all user-ids that match the provided [SelectUserId] filter. The provided + * Revoke all user-ids that match the provided [predicate]. The provided * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures * subpackets. * @@ -415,33 +343,6 @@ interface SecretKeyRingEditorInterface { * * See [RevocationAttributes.Reason] for more information. * - * @param selector user-id selector - * @param protector protector to unlock the primary secret key - * @param callback callback to modify the revocations subpackets - * @return builder - * @throws PGPException in case we cannot generate a revocation signature for the user-id - */ - @Throws(PGPException::class) - @Deprecated( - "Use of SelectUserId class is deprecated.", - ReplaceWith("revokeUserIds(protector, callback, predicate)")) - fun revokeUserIds( - selector: SelectUserId, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback? - ) = revokeUserIds(protector, callback, selector) - - /** - * Revoke all user-ids that match the provided [SelectUserId] filter. The provided - * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures - * subpackets. - * - * Note: If you intend to re-certify these user-ids at a later point, make sure to set a soft - * revocation reason in the revocation signatures hashed subpacket area using the callback. - * - * See [RevocationAttributes.Reason] for more information. - * - * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @param predicate to select user-ids for revocation * @return builder @@ -449,7 +350,6 @@ interface SecretKeyRingEditorInterface { */ @Throws(PGPException::class) fun revokeUserIds( - protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?, predicate: (String) -> Boolean ): SecretKeyRingEditorInterface @@ -459,15 +359,28 @@ interface SecretKeyRingEditorInterface { * expire, then an expiration date of null is expected. * * @param expiration new expiration date or null - * @param protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a new self-signature with the changed * expiration date */ @Throws(PGPException::class) - fun setExpirationDate( + fun setExpirationDate(expiration: Date?): SecretKeyRingEditorInterface + + /** + * Set the expiration date for the subkey identified by the given [KeyIdentifier] to the given + * expiration date. If the key is supposed to never expire, then an expiration date of null is + * expected. + * + * @param expiration new expiration date of null + * @param keyIdentifier identifier of the subkey + * @return the builder + * @throws PGPException in case we cannot generate a new subkey-binding or self-signature with + * the changed expiration date + */ + @Throws(PGPException::class) + fun setExpirationDateOfSubkey( expiration: Date?, - protector: SecretKeyRingProtector + keyIdentifier: KeyIdentifier ): SecretKeyRingEditorInterface /** @@ -476,31 +389,26 @@ interface SecretKeyRingEditorInterface { * * @param expiration new expiration date of null * @param keyId id of the subkey - * @param protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a new subkey-binding or self-signature with * the changed expiration date */ @Throws(PGPException::class) - fun setExpirationDateOfSubkey( - expiration: Date?, - keyId: Long, - protector: SecretKeyRingProtector - ): SecretKeyRingEditorInterface + @Deprecated("Pass in the subkeys KeyIdentifier instead.") + fun setExpirationDateOfSubkey(expiration: Date?, keyId: Long): SecretKeyRingEditorInterface = + setExpirationDateOfSubkey(expiration, KeyIdentifier(keyId)) /** * Create a minimal, self-authorizing revocation certificate, containing only the primary key * and a revocation signature. This type of revocation certificates was introduced in OpenPGP * v6. This method has no side effects on the original key and will leave it intact. * - * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation (key revocation) * @return minimal revocation certificate * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) fun createMinimalRevocationCertificate( - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? ): PGPPublicKeyRing @@ -508,14 +416,25 @@ interface SecretKeyRingEditorInterface { * Create a detached revocation certificate, which can be used to revoke the whole key. The * original key will not be modified by this method. * - * @param protector protector to unlock the primary key. + * @param revocationAttributes reason for the revocation + * @return revocation certificate + * @throws PGPException in case we cannot generate a revocation certificate + */ + @Throws(PGPException::class) + fun createRevocation(revocationAttributes: RevocationAttributes?): PGPSignature + + /** + * Create a detached revocation certificate, which can be used to revoke the specified subkey. + * The original key will not be modified by this method. + * + * @param subkeyIdentifier identifier of the subkey to be revoked * @param revocationAttributes reason for the revocation * @return revocation certificate * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) fun createRevocation( - protector: SecretKeyRingProtector, + subkeyIdentifier: KeyIdentifier, revocationAttributes: RevocationAttributes? ): PGPSignature @@ -524,51 +443,63 @@ interface SecretKeyRingEditorInterface { * The original key will not be modified by this method. * * @param subkeyId id of the subkey to be revoked - * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation * @return revocation certificate * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) + @Deprecated("Pass in the subkeys KeyIdentifier instead.") fun createRevocation( subkeyId: Long, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? - ): PGPSignature + ): PGPSignature = createRevocation(KeyIdentifier(subkeyId), revocationAttributes) /** * Create a detached revocation certificate, which can be used to revoke the specified subkey. * The original key will not be modified by this method. * - * @param subkeyId id of the subkey to be revoked - * @param protector protector to unlock the primary key. + * @param subkeyIdentifier identifier of the subkey to be revoked * @param callback callback to modify the subpackets of the revocation certificate. * @return revocation certificate * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) fun createRevocation( - subkeyId: Long, - protector: SecretKeyRingProtector, + subkeyIdentifier: KeyIdentifier, callback: RevocationSignatureSubpackets.Callback? ): PGPSignature + /** + * Create a detached revocation certificate, which can be used to revoke the specified subkey. + * The original key will not be modified by this method. + * + * @param subkeyId id of the subkey to be revoked + * @param callback callback to modify the subpackets of the revocation certificate. + * @return revocation certificate + * @throws PGPException in case we cannot generate a revocation certificate + */ + @Throws(PGPException::class) + @Deprecated("Pass in the subkeys KeyIdentifier instead.") + fun createRevocation( + subkeyId: Long, + callback: RevocationSignatureSubpackets.Callback? + ): PGPSignature = createRevocation(KeyIdentifier(subkeyId), callback) + /** * Create a detached revocation certificate, which can be used to revoke the specified subkey. * The original key will not be modified by this method. * * @param subkeyFingerprint fingerprint of the subkey to be revoked - * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation * @return revocation certificate * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) + @Deprecated("Pass in the subkey KeyIdentifier instead.") fun createRevocation( subkeyFingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? - ): PGPSignature + ): PGPSignature = createRevocation(subkeyFingerprint.keyIdentifier, revocationAttributes) /** * Change the passphrase of the whole key ring. @@ -676,11 +607,7 @@ interface SecretKeyRingEditorInterface { * * @return the key */ - fun done(): PGPSecretKeyRing + fun done(): OpenPGPKey - fun addSubKey( - keySpec: KeySpec, - subkeyPassphrase: Passphrase, - protector: SecretKeyRingProtector - ): SecretKeyRingEditorInterface + fun addSubkey(keySpec: KeySpec, subkeyPassphrase: Passphrase): SecretKeyRingEditorInterface } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java index cf34f9fe..f8dfc12a 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java @@ -109,7 +109,7 @@ public class UserIdRevocationTest { .forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE); assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys) - .revokeSubKey(1L, protector)); + .revokeSubkey(1L, protector)); } @Test diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java index e640302e..dc063cb4 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java @@ -38,7 +38,7 @@ public class RefuseToAddWeakSubkeyTest { KeySpec spec = KeySpec.getBuilder(KeyType.RSA(RsaLength._1024), KeyFlag.ENCRYPT_COMMS).build(); assertThrows(IllegalArgumentException.class, () -> - editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())); + editor.addSubkey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())); } @Test @@ -75,7 +75,7 @@ public class RefuseToAddWeakSubkeyTest { .setKeyCreationDate(editor.getReferenceTime()) // The key gets created after we instantiate the editor. .build(); - secretKeys = editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()) + secretKeys = editor.addSubkey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()) .done(); assertEquals(2, PGPainless.inspectKeyRing(secretKeys).getEncryptionSubkeys(EncryptionPurpose.ANY).size()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java index 729c2151..8bdce950 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java @@ -56,7 +56,7 @@ public class RevokeSubKeyTest { .forKey(secretKeys, Passphrase.fromPassword("password123")); secretKeys = PGPainless.modifyKeyRing(secretKeys) - .revokeSubKey(new OpenPgpV4Fingerprint(subKey), protector) + .revokeSubkey(new OpenPgpV4Fingerprint(subKey), protector) .done(); keysIterator = secretKeys.iterator(); primaryKey = keysIterator.next(); @@ -133,7 +133,7 @@ public class RevokeSubKeyTest { .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey(); secretKeys = PGPainless.modifyKeyRing(secretKeys) - .revokeSubKey(encryptionSubkey.getKeyID(), protector) + .revokeSubkey(encryptionSubkey.getKeyID(), protector) .done(); encryptionSubkey = secretKeys.getPublicKey(encryptionSubkey.getKeyID()); @@ -158,7 +158,7 @@ public class RevokeSubKeyTest { .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey(); secretKeys = PGPainless.modifyKeyRing(secretKeys) - .revokeSubKey(encryptionSubkey.getKeyID(), protector, new RevocationSignatureSubpackets.Callback() { + .revokeSubkey(encryptionSubkey.getKeyID(), protector, new RevocationSignatureSubpackets.Callback() { @Override public void modifyHashedSubpackets(RevocationSignatureSubpackets hashedSubpackets) { hashedSubpackets.setRevocationReason(