mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 18:29:39 +02:00
Rework KeyAccessor
This commit is contained in:
parent
ef9fed2844
commit
7a33e84497
12 changed files with 247 additions and 139 deletions
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue