From 2b060a1273a0b9049610a72eb4fe9088c6583d9e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 2 Dec 2025 17:44:45 +0100 Subject: [PATCH] Fix existing test for decrypting with missing hardware key --- .../OpenPgpMessageInputStream.kt | 44 +++++++++---------- .../yubikey/YubikeyDataDecryptorFactory.kt | 1 - .../yubikey/YubikeyDecryptionTest.kt | 4 ++ .../YubikeyHardwareTokenBackendTest.kt | 2 + 4 files changed, 28 insertions(+), 23 deletions(-) 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 b78692ab..120e9081 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 @@ -39,6 +39,7 @@ import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature import org.bouncycastle.openpgp.api.exception.MalformedOpenPGPSignatureException import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory +import org.bouncycastle.util.encoders.Hex import org.bouncycastle.util.io.TeeInputStream import org.pgpainless.PGPainless import org.pgpainless.algorithm.CompressionAlgorithm @@ -446,9 +447,13 @@ class OpenPgpMessageInputStream( continue } + if (hasUnsupportedS2KSpecifier(secretKey)) { + continue + } + if (secretKey.hasExternalSecretKey()) { LOGGER.debug( - "Decryption key ${secretKey.keyIdentifier} is located on an external device, e.g. a smartcard.") + "Decryption key ${secretKey.keyIdentifier} is located on an external device, e.g. a smartcard (0x${Hex.toHexString(secretKey.cardSerial)})") for (hardwareTokenBackend in options.hardwareTokenBackends) { LOGGER.debug( "Attempt decryption with ${hardwareTokenBackend.getBackendName()} backend.") @@ -463,27 +468,21 @@ class OpenPgpMessageInputStream( return true } } - } - /* - if (hasUnsupportedS2KSpecifier(secretKey)) { - continue - } - - */ - - val privateKey = - try { - unlockSecretKey(secretKey, protector) - } catch (e: PGPException) { - throw WrongPassphraseException(secretKey.keyIdentifier, e) + } else { + val privateKey = + try { + unlockSecretKey(secretKey, protector) + } catch (e: PGPException) { + throw WrongPassphraseException(secretKey.keyIdentifier, e) + } + if (decryptWithPrivateKey( + esks, + privateKey.keyPair, + SubkeyIdentifier( + secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier), + pkesk)) { + return true } - if (decryptWithPrivateKey( - esks, - privateKey.keyPair, - SubkeyIdentifier( - secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier), - pkesk)) { - return true } } } @@ -584,7 +583,8 @@ class OpenPgpMessageInputStream( private fun hasUnsupportedS2KSpecifier(secretKey: OpenPGPSecretKey): Boolean { val s2k = secretKey.pgpSecretKey.s2K if (s2k != null) { - if (s2k.type in 100..110) { + // 101 is GNU_DUMMY_S2K, which we kind of support + if (s2k.type in 100..110 && s2k.type != 101) { LOGGER.debug( "Skipping PKESK because key ${secretKey.keyIdentifier} has unsupported private S2K specifier ${s2k.type}") return true diff --git a/pgpainless-yubikey/src/main/kotlin/org/pgpainless/yubikey/YubikeyDataDecryptorFactory.kt b/pgpainless-yubikey/src/main/kotlin/org/pgpainless/yubikey/YubikeyDataDecryptorFactory.kt index 1a2cd51d..8f425396 100644 --- a/pgpainless-yubikey/src/main/kotlin/org/pgpainless/yubikey/YubikeyDataDecryptorFactory.kt +++ b/pgpainless-yubikey/src/main/kotlin/org/pgpainless/yubikey/YubikeyDataDecryptorFactory.kt @@ -38,7 +38,6 @@ class YubikeyDataDecryptorFactory( @JvmStatic val LOGGER = LoggerFactory.getLogger(YubikeyDataDecryptorFactory::class.java) - val ADMIN_PIN: CharArray = "12345678".toCharArray() val USER_PIN: CharArray = "123456".toCharArray() @JvmStatic diff --git a/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyDecryptionTest.kt b/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyDecryptionTest.kt index e8c488c9..15380d89 100644 --- a/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyDecryptionTest.kt +++ b/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyDecryptionTest.kt @@ -7,6 +7,7 @@ package org.pgpainless.yubikey import com.yubico.yubikit.openpgp.KeyRef import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import org.pgpainless.PGPainless import org.pgpainless.decryption_verification.ConsumerOptions import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.util.Passphrase @@ -107,6 +108,9 @@ class YubikeyDecryptionTest : YubikeyTest() { "=XVu4\n" + "-----END PGP MESSAGE-----" + override val api: PGPainless + get() = PGPainless() + @Test fun decryptMessageWithYubikey() { val key = api.readKey().parseKey(KEY) diff --git a/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyHardwareTokenBackendTest.kt b/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyHardwareTokenBackendTest.kt index efd3151b..b5ebfbe5 100644 --- a/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyHardwareTokenBackendTest.kt +++ b/pgpainless-yubikey/src/test/kotlin/org/pgpainless/yubikey/YubikeyHardwareTokenBackendTest.kt @@ -7,6 +7,7 @@ package org.pgpainless.yubikey import org.gnupg.GnuPGDummyKeyUtil import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assumptions.assumeTrue +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class YubikeyHardwareTokenBackendTest : YubikeyTest() { @@ -21,6 +22,7 @@ class YubikeyHardwareTokenBackendTest : YubikeyTest() { } @Test + @Disabled("because yubikit-android 2.9.0 cannot extract fingerprints") fun testListKeys() { val keys = backend.listKeyFingerprints() assumeTrue(keys.isNotEmpty())