From 2d1d5580d18d5fb50cf5e3a353b871df79f2bce6 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 1 Sep 2023 13:15:22 +0200 Subject: [PATCH] Kotlin conversion: S2KUsageFix --- .../key/protection/fixes/S2KUsageFix.java | 92 ------------------- .../key/protection/fixes/package-info.java | 8 -- .../key/protection/fixes/S2KUsageFix.kt | 79 ++++++++++++++++ 3 files changed, 79 insertions(+), 100 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/S2KUsageFix.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/package-info.java create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/S2KUsageFix.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/S2KUsageFix.java deleted file mode 100644 index 24fe533a..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/S2KUsageFix.java +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.key.protection.fixes; - -import org.bouncycastle.bcpg.SecretKeyPacket; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.bouncycastle.openpgp.operator.PGPDigestCalculator; -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; - -/** - * Repair class to fix keys which use S2K usage of value {@link SecretKeyPacket#USAGE_CHECKSUM}. - * The method {@link #replaceUsageChecksumWithUsageSha1(PGPSecretKeyRing, SecretKeyRingProtector)} ensures - * that such keys are encrypted using S2K usage {@link SecretKeyPacket#USAGE_SHA1} instead. - * - * @see Related PGPainless Bug Report - * @see Related PGPainless Feature Request - * @see Related upstream BC bug report - */ -public final class S2KUsageFix { - - private S2KUsageFix() { - - } - - /** - * Repair method for keys which use S2K usage
USAGE_CHECKSUM
which is deemed insecure. - * This method fixes the private keys by changing them to
USAGE_SHA1
instead. - * - * @param keys keys - * @param protector protector to unlock and re-lock affected private keys - * @return fixed key ring - * @throws PGPException in case of a PGP error. - */ - public static PGPSecretKeyRing replaceUsageChecksumWithUsageSha1(PGPSecretKeyRing keys, SecretKeyRingProtector protector) throws PGPException { - return replaceUsageChecksumWithUsageSha1(keys, protector, false); - } - - /** - * Repair method for keys which use S2K usage
USAGE_CHECKSUM
which is deemed insecure. - * This method fixes the private keys by changing them to
USAGE_SHA1
instead. - * - * @param keys keys - * @param protector protector to unlock and re-lock affected private keys - * @param skipKeysWithMissingPassphrase if set to true, missing subkey passphrases will cause the subkey to stay unaffected. - * @return fixed key ring - * @throws PGPException in case of a PGP error. - */ - public static PGPSecretKeyRing replaceUsageChecksumWithUsageSha1(PGPSecretKeyRing keys, - SecretKeyRingProtector protector, - boolean skipKeysWithMissingPassphrase) throws PGPException { - PGPDigestCalculator digestCalculator = ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1); - for (PGPSecretKey key : keys) { - // CHECKSUM is not recommended - if (key.getS2KUsage() != SecretKeyPacket.USAGE_CHECKSUM) { - continue; - } - - long keyId = key.getKeyID(); - PBESecretKeyEncryptor encryptor = protector.getEncryptor(keyId); - if (encryptor == null) { - if (skipKeysWithMissingPassphrase) { - continue; - } - throw new WrongPassphraseException("Missing passphrase for key with ID " + Long.toHexString(keyId)); - } - - PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(key, protector); - // This constructor makes use of USAGE_SHA1 by default - PGPSecretKey fixedKey = new PGPSecretKey( - privateKey, - key.getPublicKey(), - digestCalculator, - key.isMasterKey(), - protector.getEncryptor(keyId) - ); - - // replace the original key with the fixed one - keys = PGPSecretKeyRing.insertSecretKey(keys, fixedKey); - } - return keys; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/package-info.java deleted file mode 100644 index 06c299b1..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/fixes/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Secret Key Protection Fixes. - */ -package org.pgpainless.key.protection.fixes; 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 new file mode 100644 index 00000000..aeef0654 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.protection.fixes + +import org.bouncycastle.bcpg.SecretKeyPacket +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]. + * The method [replaceUsageChecksumWithUsageSha1] ensures that such keys are encrypted using + * S2K usage [SecretKeyPacket.USAGE_SHA1] instead. + * + * @see Related PGPainless Bug Report + * @see Related PGPainless Feature Request + * @see Related upstream BC bug report + */ +class S2KUsageFix { + + companion object { + + /** + * Repair method for keys which use S2K usage
USAGE_CHECKSUM
which is deemed insecure. + * This method fixes the private keys by changing them to
USAGE_SHA1
instead. + * + * @param keys keys + * @param protector protector to unlock and re-lock affected private keys + * @param skipKeysWithMissingPassphrase if set to true, missing subkey passphrases will cause the subkey to stay unaffected. + * @return fixed key ring + * @throws PGPException in case of a PGP error. + */ + @JvmStatic + @JvmOverloads + fun replaceUsageChecksumWithUsageSha1( + keys: PGPSecretKeyRing, + protector: SecretKeyRingProtector, + skipKeysWithMissingPassphrase: Boolean = false + ): PGPSecretKeyRing { + val digestCalculator = ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1) + val keyList = mutableListOf() + for (key in keys) { + // CHECKSUM is not recommended + if (key.s2KUsage != SecretKeyPacket.USAGE_CHECKSUM) { + keyList.add(key) + continue + } + + val keyId = key.keyID + val encryptor = protector.getEncryptor(keyId) + if (encryptor == null) { + if (skipKeysWithMissingPassphrase) { + keyList.add(key) + continue + } + throw WrongPassphraseException("Missing passphrase for key with ID " + java.lang.Long.toHexString(keyId)) + } + + val privateKey = unlockSecretKey(key, protector) + // This constructor makes use of USAGE_SHA1 by default + val fixedKey = PGPSecretKey( + privateKey, + key.publicKey, + digestCalculator, + key.isMasterKey, + protector.getEncryptor(keyId) + ) + keyList.add(fixedKey) + } + return PGPSecretKeyRing(keyList) + } + } +} \ No newline at end of file