1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 10:19:39 +02:00

Rework KeyAccessor

This commit is contained in:
Paul Schaub 2025-05-05 10:58:24 +02:00
parent 94febc33df
commit 0266d14594
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
12 changed files with 247 additions and 139 deletions

View file

@ -4,22 +4,91 @@
package org.pgpainless.algorithm
class AlgorithmSuite(
symmetricKeyAlgorithms: List<SymmetricKeyAlgorithm>?,
hashAlgorithms: List<HashAlgorithm>?,
compressionAlgorithms: List<CompressionAlgorithm>?,
aeadAlgorithms: List<AEADCipherMode>?,
features: List<Feature>
class AlgorithmSuite
private constructor(
val symmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>?,
val hashAlgorithms: Set<HashAlgorithm>?,
val compressionAlgorithms: Set<CompressionAlgorithm>?,
val aeadAlgorithms: Set<AEADCipherMode>?,
val features: Set<Feature>?
) {
val symmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>? = symmetricKeyAlgorithms?.toSet()
val hashAlgorithms: Set<HashAlgorithm>? = hashAlgorithms?.toSet()
val compressionAlgorithms: Set<CompressionAlgorithm>? = compressionAlgorithms?.toSet()
val aeadAlgorithms: Set<AEADCipherMode>? = aeadAlgorithms?.toSet()
val features: Set<Feature> = features.toSet()
constructor(
symmetricKeyAlgorithms: List<SymmetricKeyAlgorithm>?,
hashAlgorithms: List<HashAlgorithm>?,
compressionAlgorithms: List<CompressionAlgorithm>?,
aeadAlgorithms: List<AEADCipherMode>?,
features: List<Feature>?
) : this(
symmetricKeyAlgorithms?.toSet(),
hashAlgorithms?.toSet(),
compressionAlgorithms?.toSet(),
aeadAlgorithms?.toSet(),
features?.toSet())
fun modify(): Builder = Builder(this)
class Builder(suite: AlgorithmSuite? = null) {
private var symmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>? =
suite?.symmetricKeyAlgorithms
private var hashAlgorithms: Set<HashAlgorithm>? = suite?.hashAlgorithms
private var compressionAlgorithms: Set<CompressionAlgorithm>? = suite?.compressionAlgorithms
private var aeadAlgorithms: Set<AEADCipherMode>? = suite?.aeadAlgorithms
private var features: Set<Feature>? = suite?.features
fun overrideSymmetricKeyAlgorithms(
vararg symmetricKeyAlgorithms: SymmetricKeyAlgorithm
): Builder = overrideSymmetricKeyAlgorithms(symmetricKeyAlgorithms.toSet())
fun overrideSymmetricKeyAlgorithms(
symmetricKeyAlgorithms: Collection<SymmetricKeyAlgorithm>?
): Builder = apply { this.symmetricKeyAlgorithms = symmetricKeyAlgorithms?.toSet() }
fun overrideHashAlgorithms(vararg hashAlgorithms: HashAlgorithm): Builder =
overrideHashAlgorithms(hashAlgorithms.toSet())
fun overrideHashAlgorithms(hashAlgorithms: Collection<HashAlgorithm>?): Builder = apply {
this.hashAlgorithms = hashAlgorithms?.toSet()
}
fun overrideCompressionAlgorithms(
vararg compressionAlgorithms: CompressionAlgorithm
): Builder = overrideCompressionAlgorithms(compressionAlgorithms.toSet())
fun overrideCompressionAlgorithms(
compressionAlgorithms: Collection<CompressionAlgorithm>?
): Builder = apply { this.compressionAlgorithms = compressionAlgorithms?.toSet() }
fun overrideAeadAlgorithms(vararg aeadAlgorithms: AEADCipherMode): Builder =
overrideAeadAlgorithms(aeadAlgorithms.toSet())
fun overrideAeadAlgorithms(aeadAlgorithms: Collection<AEADCipherMode>?): Builder = apply {
this.aeadAlgorithms = aeadAlgorithms?.toSet()
}
fun overrideFeatures(vararg features: Feature): Builder = overrideFeatures(features.toSet())
fun overrideFeatures(features: Collection<Feature>?): Builder = apply {
this.features = features?.toSet()
}
fun build(): AlgorithmSuite {
return AlgorithmSuite(
symmetricKeyAlgorithms,
hashAlgorithms,
compressionAlgorithms,
aeadAlgorithms,
features)
}
}
companion object {
@JvmStatic
fun emptyBuilder(): Builder {
return Builder()
}
@JvmStatic
val defaultSymmetricKeyAlgorithms =
listOf(

View file

@ -32,10 +32,10 @@ fun OpenPGPKeyGenerator.setAlgorithmSuite(algorithms: AlgorithmSuite): OpenPGPKe
fun OpenPGPKeyGenerator.setDefaultFeatures(
critical: Boolean = true,
features: Set<Feature>
features: Set<Feature>?
): OpenPGPKeyGenerator {
this.setDefaultFeatures {
val b = Feature.toBitmask(*features.toTypedArray())
val b = features?.let { f -> Feature.toBitmask(*f.toTypedArray()) } ?: 0
it.apply { setFeature(critical, b) }
}
return this

View file

@ -33,9 +33,9 @@ class MessageMetadata(val message: Message) {
* The [SymmetricKeyAlgorithm] of the outermost encrypted data packet, or null if message is
* unencrypted.
*/
@Deprecated("Deprecated in favor of encryptionMechanism",
replaceWith = ReplaceWith("encryptionMechanism")
)
@Deprecated(
"Deprecated in favor of encryptionMechanism",
replaceWith = ReplaceWith("encryptionMechanism"))
val encryptionAlgorithm: SymmetricKeyAlgorithm?
get() = encryptionAlgorithms.let { if (it.hasNext()) it.next() else null }
@ -48,9 +48,9 @@ class MessageMetadata(val message: Message) {
* item that of the next nested encrypted data packet and so on. The iterator might also be
* empty, in case of an unencrypted message.
*/
@Deprecated("Deprecated in favor of encryptionMechanisms",
replaceWith = ReplaceWith("encryptionMechanisms")
)
@Deprecated(
"Deprecated in favor of encryptionMechanisms",
replaceWith = ReplaceWith("encryptionMechanisms"))
val encryptionAlgorithms: Iterator<SymmetricKeyAlgorithm>
get() = encryptionLayers.asSequence().map { it.algorithm }.iterator()
@ -60,7 +60,9 @@ class MessageMetadata(val message: Message) {
val isEncrypted: Boolean
get() =
if (encryptionMechanism == null) false
else encryptionMechanism!!.symmetricKeyAlgorithm != SymmetricKeyAlgorithm.NULL.algorithmId
else
encryptionMechanism!!.symmetricKeyAlgorithm !=
SymmetricKeyAlgorithm.NULL.algorithmId
fun isEncryptedFor(cert: OpenPGPCertificate): Boolean {
return encryptionLayers.asSequence().any {

View file

@ -24,7 +24,6 @@ import org.pgpainless.util.Passphrase
class EncryptionOptions(private val purpose: EncryptionPurpose, private val api: PGPainless) {
private val _encryptionMethods: MutableSet<PGPKeyEncryptionMethodGenerator> = mutableSetOf()
private val _encryptionKeys: MutableSet<OpenPGPComponentKey> = mutableSetOf()
private val _encryptionKeyIdentifiers: MutableSet<SubkeyIdentifier> = mutableSetOf()
private val _keyRingInfo: MutableMap<SubkeyIdentifier, KeyRingInfo> = mutableMapOf()
private val _keyViews: MutableMap<SubkeyIdentifier, KeyAccessor> = mutableMapOf()
private val encryptionKeySelector: EncryptionKeySelector = encryptToAllCapableSubkeys()
@ -37,7 +36,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose, private val api:
get() = _encryptionMethods.toSet()
val encryptionKeyIdentifiers
get() = _encryptionKeyIdentifiers.toSet()
get() = _encryptionKeys.map { SubkeyIdentifier(it) }
val encryptionKeys
get() = _encryptionKeys.toSet()
@ -201,7 +200,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose, private val api:
for (subkey in subkeys) {
val keyId = SubkeyIdentifier(subkey)
_keyRingInfo[keyId] = info
_keyViews[keyId] = KeyAccessor.ViaUserId(info, keyId, userId.toString())
_keyViews[keyId] = KeyAccessor.ViaUserId(subkey, cert.getUserId(userId.toString()))
addRecipientKey(subkey, false)
}
}
@ -320,14 +319,13 @@ class EncryptionOptions(private val purpose: EncryptionPurpose, private val api:
for (subkey in encryptionSubkeys) {
val keyId = SubkeyIdentifier(subkey)
_keyRingInfo[keyId] = info
_keyViews[keyId] = KeyAccessor.ViaKeyId(info, keyId)
_keyViews[keyId] = KeyAccessor.ViaKeyIdentifier(subkey)
addRecipientKey(subkey, wildcardKeyId)
}
}
private fun addRecipientKey(key: OpenPGPComponentKey, wildcardRecipient: Boolean) {
_encryptionKeys.add(key)
_encryptionKeyIdentifiers.add(SubkeyIdentifier(key))
addEncryptionMethod(
api.implementation.publicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey).also {
it.setUseWildcardRecipient(wildcardRecipient)

View file

@ -14,6 +14,7 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder
import org.bouncycastle.util.Strings
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.AlgorithmSuite
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.OpenPGPKeyVersion
import org.pgpainless.algorithm.SignatureType
@ -33,6 +34,11 @@ class KeyRingBuilder(private val version: OpenPGPKeyVersion, private val api: PG
private val userIds = mutableMapOf<String, SelfSignatureSubpackets.Callback?>()
private var passphrase = Passphrase.emptyPassphrase()
private var expirationDate: Date? = Date(System.currentTimeMillis() + (5 * MILLIS_IN_YEAR))
private var algorithmSuite: AlgorithmSuite = api.algorithmPolicy.keyGenerationAlgorithmSuite
override fun withPreferences(preferences: AlgorithmSuite): KeyRingBuilder = apply {
algorithmSuite = preferences
}
override fun setPrimaryKey(keySpec: KeySpec): KeyRingBuilder = apply {
verifyKeySpecCompliesToPolicy(keySpec, api.algorithmPolicy)
@ -95,14 +101,32 @@ class KeyRingBuilder(private val version: OpenPGPKeyVersion, private val api: PG
val signer = buildContentSigner(certKey)
val signatureGenerator = PGPSignatureGenerator(signer, certKey.publicKey)
val hashedSubPacketGenerator = primaryKeySpec!!.subpacketGenerator
hashedSubPacketGenerator.setAppropriateIssuerInfo(certKey.publicKey, version)
expirationDate?.let { hashedSubPacketGenerator.setKeyExpirationTime(certKey.publicKey, it) }
val hashedSignatureSubpackets: SignatureSubpackets =
SignatureSubpackets.createHashedSubpackets(certKey.publicKey).apply {
setKeyFlags(primaryKeySpec!!.keyFlags)
(primaryKeySpec!!.preferredHashAlgorithmsOverride ?: algorithmSuite.hashAlgorithms)
?.let { setPreferredHashAlgorithms(it) }
(primaryKeySpec!!.preferredCompressionAlgorithmsOverride
?: algorithmSuite.compressionAlgorithms)
?.let { setPreferredCompressionAlgorithms(it) }
(primaryKeySpec!!.preferredSymmetricAlgorithmsOverride
?: algorithmSuite.symmetricKeyAlgorithms)
?.let { setPreferredSymmetricKeyAlgorithms(it) }
(primaryKeySpec!!.preferredAEADAlgorithmsOverride ?: algorithmSuite.aeadAlgorithms)
?.let { setPreferredAEADCiphersuites(it) }
(primaryKeySpec!!.featuresOverride ?: algorithmSuite.features)?.let {
setFeatures(*it.toTypedArray())
}
}
expirationDate?.let {
hashedSignatureSubpackets.setKeyExpirationTime(certKey.publicKey, it)
}
if (userIds.isNotEmpty()) {
hashedSubPacketGenerator.setPrimaryUserId()
hashedSignatureSubpackets.setPrimaryUserId()
}
val hashedSubPackets = hashedSubPacketGenerator.subpacketsGenerator.generate()
val hashedSubPackets = hashedSignatureSubpackets.subpacketsGenerator.generate()
val ringGenerator =
if (userIds.isEmpty()) {
PGPKeyRingGenerator(
@ -138,7 +162,7 @@ class KeyRingBuilder(private val version: OpenPGPKeyVersion, private val api: PG
val callback = additionalUserId.value
val subpackets =
if (callback == null) {
hashedSubPacketGenerator.also { it.setPrimaryUserId(null) }
hashedSignatureSubpackets.also { it.setPrimaryUserId(null) }
} else {
SignatureSubpackets.createHashedSubpackets(primaryPubKey).also {
callback.modifyHashedSubpackets(it)
@ -167,21 +191,34 @@ class KeyRingBuilder(private val version: OpenPGPKeyVersion, private val api: PG
private fun addSubKeys(primaryKey: PGPKeyPair, ringGenerator: PGPKeyRingGenerator) {
for (subKeySpec in subKeySpecs) {
val subKey = generateKeyPair(subKeySpec, version, api.implementation)
if (subKeySpec.isInheritedSubPackets) {
ringGenerator.addSubKey(subKey)
} else {
var hashedSubpackets = subKeySpec.subpackets
try {
hashedSubpackets =
addPrimaryKeyBindingSignatureIfNecessary(
primaryKey, subKey, hashedSubpackets)
} catch (e: IOException) {
throw PGPException(
"Exception while adding primary key binding signature to signing subkey.",
e)
var hashedSignatureSubpackets: SignatureSubpackets =
SignatureSubpackets.createHashedSubpackets(subKey.publicKey).apply {
setKeyFlags(subKeySpec.keyFlags)
subKeySpec.preferredHashAlgorithmsOverride?.let {
setPreferredHashAlgorithms(it)
}
subKeySpec.preferredCompressionAlgorithmsOverride?.let {
setPreferredCompressionAlgorithms(it)
}
subKeySpec.preferredSymmetricAlgorithmsOverride?.let {
setPreferredSymmetricKeyAlgorithms(it)
}
subKeySpec.preferredAEADAlgorithmsOverride?.let {
setPreferredAEADCiphersuites(it)
}
subKeySpec.featuresOverride?.let { setFeatures(*it.toTypedArray()) }
}
ringGenerator.addSubKey(subKey, hashedSubpackets, null)
var hashedSubpackets: PGPSignatureSubpacketVector =
hashedSignatureSubpackets.subpacketsGenerator.generate()
try {
hashedSubpackets =
addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets)
} catch (e: IOException) {
throw PGPException(
"Exception while adding primary key binding signature to signing subkey.", e)
}
ringGenerator.addSubKey(subKey, hashedSubpackets, null)
}
}

View file

@ -9,10 +9,13 @@ import java.security.NoSuchAlgorithmException
import java.util.*
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.pgpainless.algorithm.AlgorithmSuite
import org.pgpainless.util.Passphrase
interface KeyRingBuilderInterface<B : KeyRingBuilderInterface<B>> {
fun withPreferences(preferences: AlgorithmSuite): B
fun setPrimaryKey(keySpec: KeySpec): B
fun setPrimaryKey(builder: KeySpecBuilder): B = setPrimaryKey(builder.build())

View file

@ -5,22 +5,25 @@
package org.pgpainless.key.generation
import java.util.*
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector
import org.pgpainless.algorithm.AEADCipherMode
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.Feature
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.key.generation.type.KeyType
import org.pgpainless.signature.subpackets.SignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper
data class KeySpec(
val keyType: KeyType,
val subpacketGenerator: SignatureSubpackets,
val isInheritedSubPackets: Boolean,
val keyFlags: List<KeyFlag>,
val preferredCompressionAlgorithmsOverride: Set<CompressionAlgorithm>?,
val preferredHashAlgorithmsOverride: Set<HashAlgorithm>?,
val preferredSymmetricAlgorithmsOverride: Set<SymmetricKeyAlgorithm>?,
val preferredAEADAlgorithmsOverride: Set<AEADCipherMode>?,
val featuresOverride: Set<Feature>?,
val keyCreationDate: Date?
) {
val subpackets: PGPSignatureSubpacketVector
get() = SignatureSubpacketsHelper.toVector(subpacketGenerator)
companion object {
@JvmStatic
fun getBuilder(type: KeyType, vararg flags: KeyFlag) = KeySpecBuilder(type, *flags)

View file

@ -5,11 +5,8 @@
package org.pgpainless.key.generation
import java.util.*
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.*
import org.pgpainless.key.generation.type.KeyType
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
class KeySpecBuilder(
@ -17,16 +14,11 @@ class KeySpecBuilder(
private val keyFlags: List<KeyFlag>,
) : KeySpecBuilderInterface {
private val hashedSubpackets: SelfSignatureSubpackets = SignatureSubpackets()
private val algorithmSuite: AlgorithmSuite =
PGPainless.getInstance().algorithmPolicy.keyGenerationAlgorithmSuite
private var preferredCompressionAlgorithms: Set<CompressionAlgorithm>? =
algorithmSuite.compressionAlgorithms
private var preferredHashAlgorithms: Set<HashAlgorithm>? = algorithmSuite.hashAlgorithms
private var preferredSymmetricAlgorithms: Set<SymmetricKeyAlgorithm>? =
algorithmSuite.symmetricKeyAlgorithms
private var preferredAEADAlgorithms: Set<AEADCipherMode>? = algorithmSuite.aeadAlgorithms
private var features: Set<Feature>? = algorithmSuite.features
private var preferredCompressionAlgorithms: Set<CompressionAlgorithm>? = null
private var preferredHashAlgorithms: Set<HashAlgorithm>? = null
private var preferredSymmetricAlgorithms: Set<SymmetricKeyAlgorithm>? = null
private var preferredAEADAlgorithms: Set<AEADCipherMode>? = null
private var features: Set<Feature>? = null
private var keyCreationDate: Date? = null
constructor(type: KeyType, vararg keyFlags: KeyFlag) : this(type, listOf(*keyFlags))
@ -70,15 +62,14 @@ class KeySpecBuilder(
}
override fun build(): KeySpec {
return hashedSubpackets
.apply {
setKeyFlags(keyFlags)
preferredCompressionAlgorithms?.let { setPreferredCompressionAlgorithms(it) }
preferredHashAlgorithms?.let { setPreferredHashAlgorithms(it) }
preferredSymmetricAlgorithms?.let { setPreferredSymmetricKeyAlgorithms(it) }
preferredAEADAlgorithms?.let { setPreferredAEADCiphersuites(it) }
features?.let { setFeatures(*it.toTypedArray()) }
}
.let { KeySpec(type, hashedSubpackets as SignatureSubpackets, false, keyCreationDate) }
return KeySpec(
type,
keyFlags,
preferredCompressionAlgorithms,
preferredHashAlgorithms,
preferredSymmetricAlgorithms,
preferredAEADAlgorithms,
features,
keyCreationDate)
}
}

View file

@ -4,80 +4,67 @@
package org.pgpainless.key.info
import org.bouncycastle.openpgp.PGPSignature
import java.util.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPCertificateComponent
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPIdentityComponent
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
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
import org.pgpainless.bouncycastle.extensions.toAEADCipherModes
import org.pgpainless.bouncycastle.extensions.toCompressionAlgorithms
import org.pgpainless.bouncycastle.extensions.toHashAlgorithms
import org.pgpainless.bouncycastle.extensions.toSymmetricKeyAlgorithms
abstract class KeyAccessor(protected val info: KeyRingInfo, protected val key: SubkeyIdentifier) {
abstract class KeyAccessor(
protected val key: OpenPGPComponentKey,
private val referenceTime: Date
) {
/**
* Depending on the way we address the key (key-id or user-id), return the respective
* [PGPSignature] which contains the algorithm preferences we are going to use.
*
* <p>
* If we address a key via its user-id, we want to rely on the algorithm preferences in the
* user-id certification, while we would instead rely on those in the direct-key signature if
* we'd address the key by key-id.
*
* @return signature
*/
abstract val signatureWithPreferences: PGPSignature
abstract val component: OpenPGPCertificateComponent
/** Preferred symmetric key encryption algorithms. */
val preferredSymmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>
get() =
SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(signatureWithPreferences)
component.getSymmetricCipherPreferences(referenceTime)?.toSymmetricKeyAlgorithms()
?: setOf()
/** Preferred hash algorithms. */
val preferredHashAlgorithms: Set<HashAlgorithm>
get() = SignatureSubpacketsUtil.parsePreferredHashAlgorithms(signatureWithPreferences)
get() = component.getHashAlgorithmPreferences(referenceTime)?.toHashAlgorithms() ?: setOf()
/** Preferred compression algorithms. */
val preferredCompressionAlgorithms: Set<CompressionAlgorithm>
get() =
SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(signatureWithPreferences)
component.getCompressionAlgorithmPreferences(referenceTime)?.toCompressionAlgorithms()
?: setOf()
/** Preferred AEAD algorithm suites. */
val preferredAEADCipherSuites: Set<AEADCipherMode>
get() = SignatureSubpacketsUtil.parsePreferredAEADCipherSuites(signatureWithPreferences)
get() =
component.getAEADCipherSuitePreferences(referenceTime)?.toAEADCipherModes() ?: setOf()
val features: Set<Feature>
get() =
Feature.fromBitmask(component.getFeatures(referenceTime)?.features?.toInt() ?: 0)
.toSet()
/**
* Address the key via a user-id (e.g. `Alice <alice@wonderland.lit>`). In this case we are
* sourcing preferred algorithms from the user-id certification first.
*/
class ViaUserId(info: KeyRingInfo, key: SubkeyIdentifier, private val userId: CharSequence) :
KeyAccessor(info, key) {
override val signatureWithPreferences: PGPSignature
get() =
checkNotNull(info.getLatestUserIdCertification(userId.toString())) {
"No valid user-id certification signature found for '$userId'."
}
class ViaUserId(
key: OpenPGPComponentKey,
userId: OpenPGPIdentityComponent,
referenceTime: Date = Date()
) : KeyAccessor(key, referenceTime) {
override val component: OpenPGPCertificateComponent = userId
}
/**
* Address the key via key-id. In this case we are sourcing preferred algorithms from the keys
* direct-key signature first.
*/
class ViaKeyId(info: KeyRingInfo, key: SubkeyIdentifier) : KeyAccessor(info, key) {
override val signatureWithPreferences: PGPSignature
get() {
if (key.isPrimaryKey) {
// If the key is located by Key ID, the algorithm of the primary User ID of the
// key
// provides the
// preferred symmetric algorithm.
info.primaryUserId?.let { userId ->
info.getLatestUserIdCertification(userId).let { if (it != null) return it }
}
}
return info.getCurrentSubkeyBindingSignature(key.keyIdentifier)
?: throw NoSuchElementException(
"Key does not carry acceptable self-signature signature.")
}
class ViaKeyIdentifier(key: OpenPGPComponentKey, referenceTime: Date = Date()) :
KeyAccessor(key, referenceTime) {
override val component: OpenPGPCertificateComponent = key
}
}

View file

@ -250,8 +250,22 @@ class SecretKeyRingEditor(
val callback =
object : SelfSignatureSubpackets.Callback {
override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) {
SignatureSubpacketsHelper.applyFrom(
keySpec.subpackets, hashedSubpackets as SignatureSubpackets)
hashedSubpackets.apply {
setKeyFlags(keySpec.keyFlags)
keySpec.preferredHashAlgorithmsOverride?.let {
setPreferredHashAlgorithms(it)
}
keySpec.preferredCompressionAlgorithmsOverride?.let {
setPreferredCompressionAlgorithms(it)
}
keySpec.preferredSymmetricAlgorithmsOverride?.let {
setPreferredSymmetricKeyAlgorithms(it)
}
keySpec.preferredAEADAlgorithmsOverride?.let {
setPreferredAEADCiphersuites(it)
}
keySpec.featuresOverride?.let { setFeatures(*it.toTypedArray()) }
}
hashedSubpackets.setSignatureCreationTime(referenceTime)
}
}
@ -268,7 +282,7 @@ class SecretKeyRingEditor(
val keyPair = KeyRingBuilder.generateKeyPair(keySpec, version, api.implementation)
val subkeyProtector =
PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyIdentifier, subkeyPassphrase)
val keyFlags = KeyFlag.fromBitmask(keySpec.subpackets.keyFlags).toMutableList()
val keyFlags = keySpec.keyFlags.toMutableList()
return addSubKey(
keyPair,
callback,

View file

@ -30,6 +30,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.AEADAlgorithm;
import org.pgpainless.algorithm.AEADCipherMode;
import org.pgpainless.algorithm.AlgorithmSuite;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.KeyFlag;
@ -323,9 +324,11 @@ public class EncryptDecryptTest {
public void testEncryptToOnlyV4CertWithOnlySEIPD1Feature() throws PGPException, IOException {
PGPainless api = PGPainless.getInstance();
OpenPGPKey v4Key = api.buildKey(OpenPGPKeyVersion.v4)
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_192)
.overrideFeatures(Feature.MODIFICATION_DETECTION)) // the key only supports SEIPD1
.withPreferences(AlgorithmSuite.emptyBuilder()
.overrideSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_192)
.overrideFeatures(Feature.MODIFICATION_DETECTION)
.build())
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
.addSubkey(KeySpec.getBuilder(KeyType.XDH_LEGACY(XDHLegacySpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
.build();
@ -358,9 +361,11 @@ public class EncryptDecryptTest {
public void testEncryptToOnlyV6CertWithOnlySEIPD2Features() throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
OpenPGPKey v6Key = api.buildKey(OpenPGPKeyVersion.v6)
.setPrimaryKey(KeySpec.getBuilder(KeyType.Ed25519(), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.overridePreferredAEADAlgorithms(new AEADCipherMode(AEADAlgorithm.OCB, SymmetricKeyAlgorithm.AES_128))
.overrideFeatures(Feature.MODIFICATION_DETECTION_2)) // the key only supports SEIPD2
.withPreferences(AlgorithmSuite.emptyBuilder()
.overrideFeatures(Feature.MODIFICATION_DETECTION_2)
.overrideAeadAlgorithms(new AEADCipherMode(AEADAlgorithm.OCB, SymmetricKeyAlgorithm.AES_192))
.build())
.setPrimaryKey(KeySpec.getBuilder(KeyType.Ed25519(), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
.addSubkey(KeySpec.getBuilder(KeyType.X25519(), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
.build();

View file

@ -12,10 +12,10 @@ import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.AlgorithmSuite;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.algorithm.OpenPGPKeyVersion;
import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve;
@ -25,12 +25,11 @@ public class GuessPreferredHashAlgorithmTest {
@Test
public void guessPreferredHashAlgorithmsAssumesHashAlgoUsedBySelfSig() {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
PGPainless api = PGPainless.getInstance();
PGPSecretKeyRing secretKeys = api.buildKey(OpenPGPKeyVersion.v4)
.withPreferences(AlgorithmSuite.emptyBuilder().build())
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519),
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.overridePreferredHashAlgorithms(new HashAlgorithm[] {})
.overridePreferredSymmetricKeyAlgorithms(new SymmetricKeyAlgorithm[] {})
.overridePreferredCompressionAlgorithms(new CompressionAlgorithm[] {}))
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
.addUserId("test@test.test")
.build()
.getPGPSecretKeyRing();