From ea3502c8c6e32483833aeeac9626b0cdaa2011d5 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 12 Sep 2023 14:35:29 +0200 Subject: [PATCH] Add PGPSecretKey.unlock() methods --- .../extensions/PGPSecretKeyExtensions.kt | 43 +++++++++++++++++++ .../OpenPgpMessageInputStream.kt | 8 ++-- .../encryption_signing/SigningOptions.kt | 10 ++--- .../key/generation/KeyRingBuilder.kt | 4 +- .../key/protection/fixes/S2KUsageFix.kt | 4 +- .../org/pgpainless/key/util/KeyRingUtils.kt | 6 ++- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt index cba7bdba..f5d4f522 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt @@ -5,7 +5,50 @@ package org.bouncycastle.extensions import org.bouncycastle.bcpg.S2K +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPPrivateKey import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor +import org.pgpainless.exception.KeyIntegrityException +import org.pgpainless.exception.WrongPassphraseException +import org.pgpainless.key.protection.SecretKeyRingProtector +import org.pgpainless.key.protection.UnlockSecretKey +import org.pgpainless.util.Passphrase + +/** + * Unlock the secret key to get its [PGPPrivateKey]. + * + * @param passphrase passphrase to unlock the secret key with. + * @throws PGPException if the key cannot be unlocked + * @throws KeyIntegrityException if the public key part was tampered with + * @throws WrongPassphraseException + */ +@Throws(PGPException::class, KeyIntegrityException::class) +fun PGPSecretKey.unlock(passphrase: Passphrase): PGPPrivateKey = + UnlockSecretKey.unlockSecretKey(this, passphrase) + +/** + * Unlock the secret key to get its [PGPPrivateKey]. + * + * @param protector protector to unlock the secret key. + * @throws PGPException if the key cannot be unlocked + * @throws KeyIntegrityException if the public key part was tampered with + */ +@Throws(PGPException::class, KeyIntegrityException::class) +@JvmOverloads +fun PGPSecretKey.unlock(protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()): PGPPrivateKey = + UnlockSecretKey.unlockSecretKey(this, protector) + +/** + * Unlock the secret key to get its [PGPPrivateKey]. + * + * @param decryptor decryptor to unlock the secret key. + * @throws PGPException if the key cannot be unlocked + * @throws KeyIntegrityException if the public key part was tampered with + */ +@Throws(PGPException::class, KeyIntegrityException::class) +fun PGPSecretKey.unlock(decryptor: PBESecretKeyDecryptor?): PGPPrivateKey = + UnlockSecretKey.unlockSecretKey(this, decryptor) /** * Returns indication that the secret key is encrypted. diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt index 87b0847a..11d5675f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt @@ -7,6 +7,7 @@ package org.pgpainless.decryption_verification import openpgp.openPgpKeyId import org.bouncycastle.bcpg.BCPGInputStream import org.bouncycastle.bcpg.UnsupportedPacketVersionException +import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory @@ -21,7 +22,6 @@ 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.KeyRingUtils import org.pgpainless.policy.Policy import org.pgpainless.signature.SignatureUtils @@ -302,7 +302,7 @@ class OpenPgpMessageInputStream( continue } - val privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector) + val privateKey = secretKey.unlock(protector) if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { return true } @@ -325,7 +325,7 @@ class OpenPgpMessageInputStream( continue } - val privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector) + val privateKey = secretKey.unlock(protector) if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { return true } @@ -351,7 +351,7 @@ class OpenPgpMessageInputStream( LOGGER.debug("Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.") val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue - val privateKey = UnlockSecretKey.unlockSecretKey(secretKey, protector) + val privateKey = secretKey.unlock(protector) if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { return true } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt index b9208ed5..512e074b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt @@ -4,6 +4,7 @@ package org.pgpainless.encryption_signing +import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.pgpainless.PGPainless.Companion.getPolicy import org.pgpainless.PGPainless.Companion.inspectKeyRing @@ -17,7 +18,6 @@ import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.OpenPgpFingerprint.Companion.of import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.protection.SecretKeyRingProtector -import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey import org.pgpainless.policy.Policy import org.pgpainless.signature.subpackets.BaseSignatureSubpackets.Callback import org.pgpainless.signature.subpackets.SignatureSubpackets @@ -157,7 +157,7 @@ class SigningOptions { for (signingPubKey in signingPubKeys) { val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) - val signingSubkey: PGPPrivateKey = unlockSecretKey(signingSecKey, signingKeyProtector) + val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) @@ -199,7 +199,7 @@ class SigningOptions { val signingSecKey = signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) - val signingSubkey = unlockSecretKey(signingSecKey, signingKeyProtector) + val signingSubkey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback) @@ -292,7 +292,7 @@ class SigningOptions { for (signingPubKey in signingPubKeys) { val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) - val signingSubkey: PGPPrivateKey = unlockSecretKey(signingSecKey, signingKeyProtector) + val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) @@ -332,7 +332,7 @@ class SigningOptions { if (signingPubKey.keyID == keyId) { val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) - val signingSubkey: PGPPrivateKey = unlockSecretKey(signingSecKey, signingKeyProtector) + val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketsCallback) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt index f6967bd6..670edd46 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt @@ -4,6 +4,7 @@ package org.pgpainless.key.generation +import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor @@ -14,7 +15,6 @@ import org.pgpainless.PGPainless import org.pgpainless.algorithm.KeyFlag import org.pgpainless.algorithm.SignatureType import org.pgpainless.implementation.ImplementationFactory -import org.pgpainless.key.protection.UnlockSecretKey import org.pgpainless.policy.Policy import org.pgpainless.provider.ProviderFactory import org.pgpainless.signature.subpackets.SelfSignatureSubpackets @@ -137,7 +137,7 @@ class KeyRingBuilder : KeyRingBuilderInterface { // Attempt to add additional user-ids to the primary public key var primaryPubKey = secretKeys.next().publicKey - val privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.secretKey, secretKeyDecryptor) + val privateKey = secretKeyRing.secretKey.unlock(secretKeyDecryptor) val userIdIterator = userIds.entries.iterator() if (userIdIterator.hasNext()) { userIdIterator.next() // Skip primary userId diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt index aeef0654..c02486ac 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt @@ -5,13 +5,13 @@ package org.pgpainless.key.protection.fixes import org.bouncycastle.bcpg.SecretKeyPacket +import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKeyRing import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.exception.WrongPassphraseException import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.protection.SecretKeyRingProtector -import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey /** * Repair class to fix keys which use S2K usage of value [SecretKeyPacket.USAGE_CHECKSUM]. @@ -62,7 +62,7 @@ class S2KUsageFix { throw WrongPassphraseException("Missing passphrase for key with ID " + java.lang.Long.toHexString(keyId)) } - val privateKey = unlockSecretKey(key, protector) + val privateKey = key.unlock(protector) // This constructor makes use of USAGE_SHA1 by default val fixedKey = PGPSecretKey( privateKey, diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt index d3c77ab6..15132f9a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt @@ -8,12 +8,12 @@ import openpgp.openPgpKeyId import org.bouncycastle.bcpg.S2K import org.bouncycastle.bcpg.SecretKeyPacket import org.bouncycastle.extensions.certificate +import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.bouncycastle.util.Strings import org.pgpainless.exception.MissingPassphraseException import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.protection.SecretKeyRingProtector -import org.pgpainless.key.protection.UnlockSecretKey import org.pgpainless.key.protection.fixes.S2KUsageFix import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -164,8 +164,10 @@ class KeyRingUtils { * @throws PGPException if something goes wrong (e.g. wrong passphrase) */ @JvmStatic + @Deprecated("Deprecated in favor of secretKey.unlock(protector)", + ReplaceWith("secretKey.unlock(protector)")) fun unlockSecretKey(secretKey: PGPSecretKey, protector: SecretKeyRingProtector): PGPPrivateKey { - return UnlockSecretKey.unlockSecretKey(secretKey, protector) + return secretKey.unlock(protector) } /**