mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 18:29:39 +02:00
Port EncryptionOptions over to OpenPGPCertificate
This commit is contained in:
parent
5ce7b05266
commit
cdc51c0037
4 changed files with 165 additions and 53 deletions
|
@ -204,7 +204,7 @@ class PGPainless(
|
|||
fun inspectKeyRing(key: PGPKeyRing, referenceTime: Date = Date()) =
|
||||
KeyRingInfo(key, referenceTime)
|
||||
|
||||
fun inspectKeyRing(key: OpenPGPKey, referenceTime: Date = Date()) =
|
||||
fun inspectKeyRing(key: OpenPGPCertificate, referenceTime: Date = Date()) =
|
||||
KeyRingInfo(key, getPolicy(), referenceTime)
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,14 +5,16 @@
|
|||
package org.pgpainless.encryption_signing
|
||||
|
||||
import java.util.*
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
|
||||
import org.pgpainless.PGPainless.Companion.inspectKeyRing
|
||||
import org.pgpainless.algorithm.EncryptionPurpose
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||
import org.pgpainless.authentication.CertificateAuthority
|
||||
import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate
|
||||
import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector
|
||||
import org.pgpainless.exception.KeyException
|
||||
import org.pgpainless.exception.KeyException.*
|
||||
import org.pgpainless.implementation.ImplementationFactory
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
|
@ -50,10 +52,10 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
constructor() : this(EncryptionPurpose.ANY)
|
||||
|
||||
/**
|
||||
* Factory method to create an [EncryptionOptions] object which will encrypt for keys which
|
||||
* carry the flag [org.pgpainless.algorithm.KeyFlag.ENCRYPT_COMMS].
|
||||
* Set the evaluation date for certificate evaluation.
|
||||
*
|
||||
* @return encryption options
|
||||
* @param evaluationDate reference time
|
||||
* @return this
|
||||
*/
|
||||
fun setEvaluationDate(evaluationDate: Date) = apply { this.evaluationDate = evaluationDate }
|
||||
|
||||
|
@ -81,7 +83,9 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
authority
|
||||
.lookupByUserId(userId, email, evaluationDate, targetAmount)
|
||||
.filter { it.isAuthenticated() }
|
||||
.forEach { addRecipient(it.certificate).also { foundAcceptable = true } }
|
||||
.forEach {
|
||||
addRecipient(it.certificate.toOpenPGPCertificate()).also { foundAcceptable = true }
|
||||
}
|
||||
require(foundAcceptable) {
|
||||
"Could not identify any trust-worthy certificates for '$userId' and target trust amount $targetAmount."
|
||||
}
|
||||
|
@ -89,11 +93,14 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
|
||||
/**
|
||||
* Add all key rings in the provided [Iterable] (e.g.
|
||||
* [org.bouncycastle.openpgp.PGPPublicKeyRingCollection]) as recipients.
|
||||
* [org.bouncycastle.openpgp.PGPPublicKeyRingCollection]) as recipients. Note: This method is
|
||||
* deprecated. Instead, repeatedly call [addRecipient], passing in individual
|
||||
* [OpenPGPCertificate] instances.
|
||||
*
|
||||
* @param keys keys
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated("Repeatedly pass OpenPGPCertificate instances instead.")
|
||||
fun addRecipients(keys: Iterable<PGPPublicKeyRing>) = apply {
|
||||
keys.toList().let {
|
||||
require(it.isNotEmpty()) { "Set of recipient keys cannot be empty." }
|
||||
|
@ -104,12 +111,15 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
/**
|
||||
* Add all key rings in the provided [Iterable] (e.g.
|
||||
* [org.bouncycastle.openpgp.PGPPublicKeyRingCollection]) as recipients. Per key ring, the
|
||||
* selector is applied to select one or more encryption subkeys.
|
||||
* selector is applied to select one or more encryption subkeys. Note: This method is
|
||||
* deprecated. Instead, repeatedly call [addRecipient], passing in individual
|
||||
* [OpenPGPCertificate] instances.
|
||||
*
|
||||
* @param keys keys
|
||||
* @param selector encryption key selector
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated("Repeatedly pass OpenPGPCertificate instances instead.")
|
||||
fun addRecipients(keys: Iterable<PGPPublicKeyRing>, selector: EncryptionKeySelector) = apply {
|
||||
keys.toList().let {
|
||||
require(it.isNotEmpty()) { "Set of recipient keys cannot be empty." }
|
||||
|
@ -117,76 +127,180 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the message to the recipients [OpenPGPCertificate].
|
||||
*
|
||||
* @param cert recipient certificate
|
||||
* @return this
|
||||
*/
|
||||
fun addRecipient(cert: OpenPGPCertificate) = addRecipient(cert, encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Add a recipient by providing a key.
|
||||
*
|
||||
* @param key key ring
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated(
|
||||
"Pass in OpenPGPCertificate instead.",
|
||||
replaceWith =
|
||||
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)"))
|
||||
fun addRecipient(key: PGPPublicKeyRing) = addRecipient(key, encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the given recipients [OpenPGPCertificate], sourcing algorithm
|
||||
* preferences by inspecting the binding signature on the passed [userId].
|
||||
*
|
||||
* @param cert recipient certificate
|
||||
* @param userId recipient user-id
|
||||
* @return this
|
||||
*/
|
||||
fun addRecipient(cert: OpenPGPCertificate, userId: CharSequence) =
|
||||
addRecipient(cert, userId, encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Add a recipient by providing a key and recipient user-id. The user-id is used to determine
|
||||
* the recipients preferences (algorithms etc.).
|
||||
* the recipients preferences (algorithms etc.). Note: This method is deprecated. Replace the
|
||||
* [PGPPublicKeyRing] instance with an [OpenPGPCertificate].
|
||||
*
|
||||
* @param key key ring
|
||||
* @param userId user id
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated(
|
||||
"Pass in OpenPGPCertificate instead.",
|
||||
replaceWith = ReplaceWith("addRecipient(key.toOpenPGPCertificate(), userId)"))
|
||||
fun addRecipient(key: PGPPublicKeyRing, userId: CharSequence) =
|
||||
addRecipient(key, userId, encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the given recipients [OpenPGPCertificate], sourcing algorithm
|
||||
* preferences by inspecting the binding signature on the given [userId] and filtering the
|
||||
* recipient subkeys through the given [EncryptionKeySelector].
|
||||
*
|
||||
* @param cert recipient certificate
|
||||
* @param userId user-id for sourcing algorithm preferences
|
||||
* @param encryptionKeySelector decides which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
fun addRecipient(
|
||||
cert: OpenPGPCertificate,
|
||||
userId: CharSequence,
|
||||
encryptionKeySelector: EncryptionKeySelector
|
||||
) = apply {
|
||||
val info = inspectKeyRing(cert, evaluationDate)
|
||||
val subkeys =
|
||||
encryptionKeySelector.selectEncryptionSubkeys(
|
||||
info.getEncryptionSubkeys(userId, purpose))
|
||||
if (subkeys.isEmpty()) {
|
||||
throw UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(cert.pgpPublicKeyRing))
|
||||
}
|
||||
|
||||
for (subkey in subkeys) {
|
||||
val keyId = SubkeyIdentifier(subkey)
|
||||
_keyRingInfo[keyId] = info
|
||||
_keyViews[keyId] = KeyAccessor.ViaUserId(info, keyId, userId.toString())
|
||||
addRecipientKey(subkey, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the message for the given recipients public key, sourcing algorithm preferences by
|
||||
* inspecting the binding signature on the given [userId] and filtering the recipient subkeys
|
||||
* through the given [EncryptionKeySelector].
|
||||
*
|
||||
* @param key recipient public key
|
||||
* @param userId user-id for sourcing algorithm preferences
|
||||
* @param encryptionKeySelector decides which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated(
|
||||
"Pass in OpenPGPCertificate instead.",
|
||||
replaceWith =
|
||||
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), userId, encryptionKeySelector)"))
|
||||
fun addRecipient(
|
||||
key: PGPPublicKeyRing,
|
||||
userId: CharSequence,
|
||||
encryptionKeySelector: EncryptionKeySelector
|
||||
) = apply {
|
||||
val info = KeyRingInfo(key, evaluationDate)
|
||||
val subkeys =
|
||||
encryptionKeySelector.selectEncryptionSubkeys(
|
||||
info.getEncryptionSubkeys(userId, purpose).map { it.pgpPublicKey })
|
||||
if (subkeys.isEmpty()) {
|
||||
throw KeyException.UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key))
|
||||
}
|
||||
) = addRecipient(key.toOpenPGPCertificate(), userId, encryptionKeySelector)
|
||||
|
||||
for (subkey in subkeys) {
|
||||
val keyId = SubkeyIdentifier(key, subkey.keyID)
|
||||
_keyRingInfo[keyId] = info
|
||||
_keyViews[keyId] = KeyAccessor.ViaUserId(info, keyId, userId.toString())
|
||||
addRecipientKey(key, subkey, false)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Encrypt the message for the given recipients [OpenPGPCertificate], filtering encryption
|
||||
* subkeys through the given [EncryptionKeySelector].
|
||||
*
|
||||
* @param cert recipient certificate
|
||||
* @param encryptionKeySelector decides, which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
fun addRecipient(cert: OpenPGPCertificate, encryptionKeySelector: EncryptionKeySelector) =
|
||||
addAsRecipient(cert, encryptionKeySelector, false)
|
||||
|
||||
fun addRecipient(key: PGPPublicKeyRing, encryptionKeySelector: EncryptionKeySelector) = apply {
|
||||
addAsRecipient(key, encryptionKeySelector, false)
|
||||
}
|
||||
/**
|
||||
* Encrypt the message for the given recipients public key, filtering encryption subkeys through
|
||||
* the given [EncryptionKeySelector].
|
||||
*
|
||||
* @param key recipient public key
|
||||
* @param encryptionKeySelector decides, which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated(
|
||||
"Pass in OpenPGPCertificate instead.",
|
||||
replaceWith =
|
||||
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)"))
|
||||
fun addRecipient(key: PGPPublicKeyRing, encryptionKeySelector: EncryptionKeySelector) =
|
||||
addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the recipients [OpenPGPCertificate], keeping the recipient anonymous
|
||||
* by setting a wildcard key-id / fingerprint.
|
||||
*
|
||||
* @param cert recipient certificate
|
||||
* @param selector decides, which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun addHiddenRecipient(
|
||||
cert: OpenPGPCertificate,
|
||||
selector: EncryptionKeySelector = encryptionKeySelector
|
||||
) = addAsRecipient(cert, selector, true)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the recipients public key, keeping the recipient anonymous by setting
|
||||
* a wildcard key-id / fingerprint.
|
||||
*
|
||||
* @param key recipient public key
|
||||
* @param selector decides, which subkeys to encrypt for
|
||||
* @return this
|
||||
*/
|
||||
@JvmOverloads
|
||||
@Deprecated(
|
||||
"Pass in an OpenPGPCertificate instead.",
|
||||
replaceWith = ReplaceWith("addHiddenRecipient(key.toOpenPGPCertificate(), selector)"))
|
||||
fun addHiddenRecipient(
|
||||
key: PGPPublicKeyRing,
|
||||
selector: EncryptionKeySelector = encryptionKeySelector
|
||||
) = apply { addAsRecipient(key, selector, true) }
|
||||
) = addHiddenRecipient(key.toOpenPGPCertificate(), selector)
|
||||
|
||||
private fun addAsRecipient(
|
||||
key: PGPPublicKeyRing,
|
||||
cert: OpenPGPCertificate,
|
||||
selector: EncryptionKeySelector,
|
||||
wildcardKeyId: Boolean
|
||||
) = apply {
|
||||
val info = KeyRingInfo(key, evaluationDate)
|
||||
val info = inspectKeyRing(cert, evaluationDate)
|
||||
val primaryKeyExpiration =
|
||||
try {
|
||||
info.primaryKeyExpirationDate
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw UnacceptableSelfSignatureException(OpenPgpFingerprint.of(key))
|
||||
throw UnacceptableSelfSignatureException(
|
||||
OpenPgpFingerprint.of(cert.pgpPublicKeyRing))
|
||||
}
|
||||
|
||||
if (primaryKeyExpiration != null && primaryKeyExpiration < evaluationDate) {
|
||||
throw ExpiredKeyException(OpenPgpFingerprint.of(key), primaryKeyExpiration)
|
||||
throw ExpiredKeyException(
|
||||
OpenPgpFingerprint.of(cert.pgpPublicKeyRing), primaryKeyExpiration)
|
||||
}
|
||||
|
||||
var encryptionSubkeys =
|
||||
selector.selectEncryptionSubkeys(
|
||||
info.getEncryptionSubkeys(purpose).map { it.pgpPublicKey })
|
||||
var encryptionSubkeys = selector.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose))
|
||||
|
||||
// There are some legacy keys around without key flags.
|
||||
// If we allow encryption for those keys, we add valid keys without any key flags, if they
|
||||
|
@ -197,31 +311,26 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
info.validSubkeys
|
||||
.filter { it.pgpPublicKey.isEncryptionKey }
|
||||
.filter { info.getKeyFlagsOf(it.keyIdentifier).isEmpty() }
|
||||
.map { it.pgpPublicKey }
|
||||
}
|
||||
|
||||
if (encryptionSubkeys.isEmpty()) {
|
||||
throw UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key))
|
||||
throw UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(cert.pgpPublicKeyRing))
|
||||
}
|
||||
|
||||
for (subkey in encryptionSubkeys) {
|
||||
val keyId = SubkeyIdentifier(key, subkey.keyID)
|
||||
val keyId = SubkeyIdentifier(subkey)
|
||||
_keyRingInfo[keyId] = info
|
||||
_keyViews[keyId] = KeyAccessor.ViaKeyId(info, keyId)
|
||||
addRecipientKey(key, subkey, wildcardKeyId)
|
||||
addRecipientKey(subkey, wildcardKeyId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRecipientKey(
|
||||
certificate: PGPPublicKeyRing,
|
||||
key: PGPPublicKey,
|
||||
wildcardKeyId: Boolean
|
||||
) {
|
||||
_encryptionKeyIdentifiers.add(SubkeyIdentifier(certificate, key.keyID))
|
||||
private fun addRecipientKey(key: OpenPGPComponentKey, wildcardKeyId: Boolean) {
|
||||
_encryptionKeyIdentifiers.add(SubkeyIdentifier(key))
|
||||
addEncryptionMethod(
|
||||
ImplementationFactory.getInstance().getPublicKeyKeyEncryptionMethodGenerator(key).also {
|
||||
it.setUseWildcardKeyID(wildcardKeyId)
|
||||
})
|
||||
ImplementationFactory.getInstance()
|
||||
.getPublicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey)
|
||||
.also { it.setUseWildcardKeyID(wildcardKeyId) })
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -295,7 +404,9 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
fun hasEncryptionMethod() = _encryptionMethods.isNotEmpty()
|
||||
|
||||
fun interface EncryptionKeySelector {
|
||||
fun selectEncryptionSubkeys(encryptionCapableKeys: List<PGPPublicKey>): List<PGPPublicKey>
|
||||
fun selectEncryptionSubkeys(
|
||||
encryptionCapableKeys: List<OpenPGPComponentKey>
|
||||
): List<OpenPGPComponentKey>
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -164,7 +164,7 @@ class CertificateValidator {
|
|||
|
||||
if (signingSubkey.keyID == primaryKey.keyID) { // signing key is primary key
|
||||
if (directKeyAndRevSigs.isNotEmpty()) {
|
||||
val directKeySig = directKeyAndRevSigs[0]!!
|
||||
val directKeySig = directKeyAndRevSigs[0]
|
||||
val flags = SignatureSubpacketsUtil.getKeyFlags(directKeySig)
|
||||
if (flags != null && KeyFlag.hasKeyFlag(flags.flags, KeyFlag.SIGN_DATA)) {
|
||||
return true
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
|||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -153,7 +154,7 @@ public class EncryptionOptionsTest {
|
|||
() -> options.addRecipient(publicKeys, new EncryptionOptions.EncryptionKeySelector() {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PGPPublicKey> selectEncryptionSubkeys(@NotNull List<? extends PGPPublicKey> encryptionCapableKeys) {
|
||||
public List<OpenPGPCertificate.OpenPGPComponentKey> selectEncryptionSubkeys(@NotNull List<? extends OpenPGPCertificate.OpenPGPComponentKey> encryptionCapableKeys) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}));
|
||||
|
@ -161,7 +162,7 @@ public class EncryptionOptionsTest {
|
|||
assertThrows(KeyException.UnacceptableEncryptionKeyException.class,
|
||||
() -> options.addRecipient(publicKeys, "test@pgpainless.org", new EncryptionOptions.EncryptionKeySelector() {
|
||||
@Override
|
||||
public List<PGPPublicKey> selectEncryptionSubkeys(@Nonnull List<? extends PGPPublicKey> encryptionCapableKeys) {
|
||||
public List<OpenPGPCertificate.OpenPGPComponentKey> selectEncryptionSubkeys(@Nonnull List<? extends OpenPGPCertificate.OpenPGPComponentKey> encryptionCapableKeys) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue