From 8b5d9af52239fb4e61367b76c88657eab42fac67 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 6 Mar 2025 10:38:58 +0100 Subject: [PATCH] buildKey(): Use BC KeyGenerator, but apply PGPainless algorithm preferences --- .../main/kotlin/org/pgpainless/PGPainless.kt | 17 +++- .../pgpainless/algorithm/AlgorithmSuite.kt | 13 +-- .../SignatureSubpacketsFunctionHelper.kt | 90 +++++++++++++++++++ .../secretkeyring/SecretKeyRingEditor.kt | 1 + 4 files changed, 108 insertions(+), 13 deletions(-) create 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 2b43958c..c7f7b8b2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -19,6 +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.decryption_verification.DecryptionBuilder import org.pgpainless.encryption_signing.EncryptionBuilder import org.pgpainless.key.certification.CertifyCertificate @@ -59,7 +60,21 @@ class PGPainless( implementation, version.numeric, version == OpenPGPKeyVersion.v6, creationTime) .apply { val genAlgs = algorithmPolicy.keyGenerationAlgorithmSuite - setDefaultFeatures(genAlgs.features.toSignatureSubpacketsFunction(true)) + // 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)) } fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt index 2012e2e1..801e674c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt @@ -4,8 +4,6 @@ package org.pgpainless.algorithm -import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction - class AlgorithmSuite( symmetricKeyAlgorithms: List?, hashAlgorithms: List?, @@ -18,16 +16,7 @@ class AlgorithmSuite( val hashAlgorithms: Set? = hashAlgorithms?.toSet() val compressionAlgorithms: Set? = compressionAlgorithms?.toSet() val aeadAlgorithms: Set? = aeadAlgorithms?.toSet() - val features: FeatureSet = FeatureSet(features.toSet()) - - class FeatureSet(val features: Set) { - fun toSignatureSubpacketsFunction(critical: Boolean = true): SignatureSubpacketsFunction { - return SignatureSubpacketsFunction { - val b = Feature.toBitmask(*features.toTypedArray()) - it.apply { setFeature(critical, b) } - } - } - } + val features: Set = features.toSet() companion object { 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 new file mode 100644 index 00000000..80feda8d --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/helpers/SignatureSubpacketsFunctionHelper.kt @@ -0,0 +1,90 @@ +// 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/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 475a68c1..71587b85 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 @@ -90,6 +90,7 @@ class SecretKeyRingEditor(var key: OpenPGPKey, override val referenceTime: Date } builder.hashedSubpackets.apply { setKeyFlags(info.getKeyFlagsOf(primaryKey.keyID)) + hashAlgorithmPreferences hashAlgorithmPreferences?.let { setPreferredHashAlgorithms(it) } symmetricKeyAlgorithmPreferences?.let { setPreferredSymmetricKeyAlgorithms(it) } compressionAlgorithmPreferences?.let { setPreferredCompressionAlgorithms(it) }