From f9c2ade2d03da95a4c6faee4cd9b9923e21cc6dc Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 6 Mar 2025 11:41:36 +0100 Subject: [PATCH] Implement applying algorithm preferences as extension functions --- .../main/kotlin/org/pgpainless/PGPainless.kt | 21 +-- .../OpenPGPKeyGeneratorExtensions.kt | 126 ++++++++++++++++++ .../SignatureSubpacketsFunctionHelper.kt | 90 ------------- .../key/generation/GenerateV6KeyTest.java | 2 + 4 files changed, 130 insertions(+), 109 deletions(-) create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/OpenPGPKeyGeneratorExtensions.kt delete mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/helpers/SignatureSubpacketsFunctionHelper.kt diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index c7f7b8b2..98ff0603 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -19,7 +19,7 @@ import org.bouncycastle.openpgp.api.OpenPGPKeyReader import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.bouncycastle.PolicyAdapter -import org.pgpainless.bouncycastle.helpers.SignatureSubpacketsFunctionHelper +import org.pgpainless.bouncycastle.extensions.setAlgorithmSuite import org.pgpainless.decryption_verification.DecryptionBuilder import org.pgpainless.encryption_signing.EncryptionBuilder import org.pgpainless.key.certification.CertifyCertificate @@ -58,24 +58,7 @@ class PGPainless( ): OpenPGPKeyGenerator = OpenPGPKeyGenerator( implementation, version.numeric, version == OpenPGPKeyVersion.v6, creationTime) - .apply { - val genAlgs = algorithmPolicy.keyGenerationAlgorithmSuite - // Set default algorithm preferences from AlgorithmSuite - setDefaultFeatures( - SignatureSubpacketsFunctionHelper.applyFeatures(true, genAlgs.features)) - setDefaultSymmetricKeyPreferences( - SignatureSubpacketsFunctionHelper.applySymmetricAlgorithmPreferences( - true, genAlgs.symmetricKeyAlgorithms)) - setDefaultHashAlgorithmPreferences( - SignatureSubpacketsFunctionHelper.applyHashAlgorithmPreferences( - true, genAlgs.hashAlgorithms)) - setDefaultCompressionAlgorithmPreferences( - SignatureSubpacketsFunctionHelper.applyCompressionAlgorithmPreferences( - true, genAlgs.compressionAlgorithms)) - setDefaultAeadAlgorithmPreferences( - SignatureSubpacketsFunctionHelper.applyAEADAlgorithmSuites( - false, genAlgs.aeadAlgorithms)) - } + .setAlgorithmSuite(algorithmPolicy.keyGenerationAlgorithmSuite) fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/OpenPGPKeyGeneratorExtensions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/OpenPGPKeyGeneratorExtensions.kt new file mode 100644 index 00000000..05a812d0 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/OpenPGPKeyGeneratorExtensions.kt @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.bouncycastle.extensions + +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator +import org.pgpainless.algorithm.AEADCipherMode +import org.pgpainless.algorithm.AlgorithmSuite +import org.pgpainless.algorithm.CompressionAlgorithm +import org.pgpainless.algorithm.Feature +import org.pgpainless.algorithm.HashAlgorithm +import org.pgpainless.algorithm.SymmetricKeyAlgorithm + +/** + * Apply different algorithm preferences (features, symmetric-key-, hash-, compression- and AEAD + * algorithm preferences to the [OpenPGPKeyGenerator] for key generation. The preferences will be + * set on preference-signatures on the generated keys. + * + * @param algorithms algorithm suite + * @return this + */ +fun OpenPGPKeyGenerator.setAlgorithmSuite(algorithms: AlgorithmSuite): OpenPGPKeyGenerator { + setDefaultFeatures(true, algorithms.features) + setDefaultSymmetricKeyPreferences(true, algorithms.symmetricKeyAlgorithms) + setDefaultHashAlgorithmPreferences(true, algorithms.hashAlgorithms) + setDefaultCompressionAlgorithmPreferences(true, algorithms.compressionAlgorithms) + setDefaultAeadAlgorithmPreferences(false, algorithms.aeadAlgorithms) + return this +} + +fun OpenPGPKeyGenerator.setDefaultFeatures( + critical: Boolean = true, + features: Set +): OpenPGPKeyGenerator { + this.setDefaultFeatures { + val b = Feature.toBitmask(*features.toTypedArray()) + it.apply { setFeature(critical, b) } + } + return this +} + +/** + * Define [SymmetricKeyAlgorithms][SymmetricKeyAlgorithm] that will be applied as symmetric key + * algorithm preferences to preference-signatures on freshly generated keys. + * + * @param critical whether to mark the preference subpacket as critical + * @param symmetricKeyAlgorithms ordered set of preferred symmetric key algorithms + * @return this + */ +fun OpenPGPKeyGenerator.setDefaultSymmetricKeyPreferences( + critical: Boolean = true, + symmetricKeyAlgorithms: Set? +): OpenPGPKeyGenerator = apply { + symmetricKeyAlgorithms?.let { algorithms -> + this.setDefaultSymmetricKeyPreferences { + val algorithmIds = algorithms.map { a -> a.algorithmId }.toIntArray() + it.apply { setPreferredSymmetricAlgorithms(critical, algorithmIds) } + } + } +} + +/** + * Define [HashAlgorithms][HashAlgorithm] that will be applied as hash algorithm preferences to + * preference-signatures on freshly generated keys. + * + * @param critical whether to mark the preference subpacket as critical + * @param hashAlgorithms ordered set of preferred hash algorithms + * @return this + */ +fun OpenPGPKeyGenerator.setDefaultHashAlgorithmPreferences( + critical: Boolean = true, + hashAlgorithms: Set? +): OpenPGPKeyGenerator = apply { + hashAlgorithms?.let { algorithms -> + this.setDefaultHashAlgorithmPreferences { + val algorithmIds = algorithms.map { a -> a.algorithmId }.toIntArray() + it.apply { setPreferredHashAlgorithms(critical, algorithmIds) } + } + } +} + +/** + * Define [CompressionAlgorithms][CompressionAlgorithm] that will be applied as compression + * algorithm preferences to preference-signatures on freshly generated keys. + * + * @param critical whether to mark the preference subpacket as critical + * @param compressionAlgorithms ordered set of preferred compression algorithms + * @return this + */ +fun OpenPGPKeyGenerator.setDefaultCompressionAlgorithmPreferences( + critical: Boolean = true, + compressionAlgorithms: Set? +): OpenPGPKeyGenerator = apply { + compressionAlgorithms?.let { algorithms -> + this.setDefaultCompressionAlgorithmPreferences { + val algorithmIds = algorithms.map { a -> a.algorithmId }.toIntArray() + it.apply { setPreferredCompressionAlgorithms(critical, algorithmIds) } + } + } +} + +/** + * Define [AEADCipherModes][AEADCipherMode] that will be applied as AEAD algorithm preferences to + * preference signatures on freshly generated keys. + * + * @param critical whether to mark the preferences subpacket as critical + * @param aeadAlgorithms ordered set of AEAD preferences + * @return this + */ +fun OpenPGPKeyGenerator.setDefaultAeadAlgorithmPreferences( + critical: Boolean = false, + aeadAlgorithms: Set? +): OpenPGPKeyGenerator = apply { + aeadAlgorithms?.let { algorithms -> + this.setDefaultAeadAlgorithmPreferences { + val builder = PreferredAEADCiphersuites.builder(critical) + for (ciphermode: AEADCipherMode in algorithms) { + builder.addCombination( + ciphermode.ciphermode.algorithmId, ciphermode.aeadAlgorithm.algorithmId) + } + it.apply { setPreferredAEADCiphersuites(builder) } + } + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/helpers/SignatureSubpacketsFunctionHelper.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/helpers/SignatureSubpacketsFunctionHelper.kt deleted file mode 100644 index 80feda8d..00000000 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/helpers/SignatureSubpacketsFunctionHelper.kt +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.bouncycastle.helpers - -import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites -import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction -import org.pgpainless.algorithm.AEADCipherMode -import org.pgpainless.algorithm.CompressionAlgorithm -import org.pgpainless.algorithm.Feature -import org.pgpainless.algorithm.HashAlgorithm -import org.pgpainless.algorithm.SymmetricKeyAlgorithm - -class SignatureSubpacketsFunctionHelper { - - companion object { - - @JvmStatic - fun applySymmetricAlgorithmPreferences( - critical: Boolean = true, - symmetricAlgorithms: Set? - ): SignatureSubpacketsFunction { - return symmetricAlgorithms?.let { algorithms -> - val algorithmIds = algorithms.map { it.algorithmId }.toIntArray() - SignatureSubpacketsFunction { - it.apply { setPreferredSymmetricAlgorithms(critical, algorithmIds) } - } - } - ?: SignatureSubpacketsFunction { it } - } - - @JvmStatic - fun applyHashAlgorithmPreferences( - critical: Boolean = true, - hashAlgorithms: Set? - ): SignatureSubpacketsFunction { - return hashAlgorithms?.let { algorithms -> - val algorithmIds = algorithms.map { it.algorithmId }.toIntArray() - SignatureSubpacketsFunction { - it.apply { setPreferredHashAlgorithms(critical, algorithmIds) } - } - } - ?: SignatureSubpacketsFunction { it } - } - - @JvmStatic - fun applyCompressionAlgorithmPreferences( - critical: Boolean = true, - compressionAlgorithms: Set? - ): SignatureSubpacketsFunction { - return compressionAlgorithms?.let { algorithms -> - val algorithmIds = algorithms.map { it.algorithmId }.toIntArray() - SignatureSubpacketsFunction { - it.apply { setPreferredCompressionAlgorithms(critical, algorithmIds) } - } - } - ?: SignatureSubpacketsFunction { it } - } - - @JvmStatic - fun applyAEADAlgorithmSuites( - critical: Boolean = true, - aeadAlgorithms: Set? - ): SignatureSubpacketsFunction { - return aeadAlgorithms?.let { algorithms -> - SignatureSubpacketsFunction { - val builder = PreferredAEADCiphersuites.builder(critical) - for (ciphermode: AEADCipherMode in algorithms) { - builder.addCombination( - ciphermode.ciphermode.algorithmId, ciphermode.aeadAlgorithm.algorithmId) - } - it.apply { setPreferredAEADCiphersuites(builder) } - } - } - ?: SignatureSubpacketsFunction { it } - } - - @JvmStatic - fun applyFeatures( - critical: Boolean = true, - features: Set - ): SignatureSubpacketsFunction { - return SignatureSubpacketsFunction { - val b = Feature.toBitmask(*features.toTypedArray()) - it.apply { setFeature(critical, b) } - } - } - } -} diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateV6KeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateV6KeyTest.java index a524eea6..a479093d 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateV6KeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateV6KeyTest.java @@ -96,6 +96,8 @@ public class GenerateV6KeyTest { OpenPGPCertificate certificate = key.toCertificate(); assertFalse(certificate.isSecretKey()); + // CHECKSTYLE:OFF System.out.println(certificate.toAsciiArmoredString()); + // CHECKSTYLE:ON } }