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

Kotlin conversion: SecretKeyRingProtector and subclasses

This commit is contained in:
Paul Schaub 2023-08-31 17:15:36 +02:00
parent c40e2ba6c2
commit 5fce443ad9
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
16 changed files with 198 additions and 215 deletions

View file

@ -0,0 +1,150 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.protection
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider
import org.pgpainless.util.Passphrase
import kotlin.jvm.Throws
/**
* Task of the [SecretKeyRingProtector] is to map encryptor/decryptor objects to key-ids.
* [PBESecretKeyEncryptor]/[PBESecretKeyDecryptor] are used to encrypt/decrypt secret keys using a passphrase.
*
* While it is easy to create an implementation of this interface that fits your needs, there are a bunch of
* implementations ready for use.
*/
interface SecretKeyRingProtector {
/**
* Returns true, if the protector has a passphrase for the key with the given key-id.
*
* @param keyId key id
* @return true if it has a passphrase, false otherwise
*/
fun hasPassphraseFor(keyId: Long): Boolean
/**
* Return a decryptor for the key of id `keyId`.
* This method returns null if the key is unprotected.
*
* @param keyId id of the key
* @return decryptor for the key
*/
@Throws(PGPException::class)
fun getDecryptor(keyId: Long): PBESecretKeyDecryptor?
/**
* Return an encryptor for the key of id `keyId`.
* This method returns null if the key is unprotected.
*
* @param keyId id of the key
* @return encryptor for the key
*/
@Throws(PGPException::class)
fun getEncryptor(keyId: Long): PBESecretKeyEncryptor?
companion object {
/**
* Return a protector for secret keys.
* The protector maintains an in-memory cache of passphrases and can be extended with new passphrases
* at runtime.
*
* See [CachingSecretKeyRingProtector] for how to memorize/forget additional passphrases during runtime.
*
* @param missingPassphraseCallback callback that is used to provide missing passphrases.
* @return caching secret key protector
*/
@JvmStatic
fun defaultSecretKeyRingProtector(
missingPassphraseCallback: SecretKeyPassphraseProvider?
): CachingSecretKeyRingProtector = CachingSecretKeyRingProtector(
mapOf(), KeyRingProtectionSettings.secureDefaultSettings(), missingPassphraseCallback)
/**
* Use the provided passphrase to lock/unlock all keys in the provided key ring.
*
* This protector will use the provided passphrase to lock/unlock all subkeys present in the provided keys object.
* For other keys that are not present in the ring, it will return null.
*
* @param passphrase passphrase
* @param keys key ring
* @return protector
*/
@JvmStatic
fun unlockEachKeyWith(passphrase: Passphrase, keys: PGPSecretKeyRing): SecretKeyRingProtector =
fromPassphraseMap(keys.map { it.keyID }.associateWith { passphrase })
/**
* Use the provided passphrase to unlock any key.
*
* @param passphrase passphrase
* @return protector
*/
@JvmStatic
fun unlockAnyKeyWith(passphrase: Passphrase): SecretKeyRingProtector =
BaseSecretKeyRingProtector(SolitaryPassphraseProvider(passphrase))
/**
* Use the provided passphrase to lock/unlock only the provided (sub-)key.
* This protector will only return a non-null encryptor/decryptor based on the provided passphrase if
* [getEncryptor]/[getDecryptor] is getting called with the key-id of the provided key.
*
* Otherwise, this protector will always return null.
*
* @param passphrase passphrase
* @param key key to lock/unlock
* @return protector
*/
@JvmStatic
fun unlockSingleKeyWith(passphrase: Passphrase, key: PGPSecretKey): SecretKeyRingProtector =
PasswordBasedSecretKeyRingProtector.forKey(key, passphrase)
/**
* Use the provided passphrase to lock/unlock only the provided (sub-)key.
* This protector will only return a non-null encryptor/decryptor based on the provided passphrase if
* [getEncryptor]/[getDecryptor] is getting called with the key-id of the provided key.
*
* Otherwise, this protector will always return null.
*
* @param passphrase passphrase
* @param keyId id of the key to lock/unlock
* @return protector
*/
@JvmStatic
fun unlockSingleKeyWith(passphrase: Passphrase, keyId: Long): SecretKeyRingProtector =
PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase)
/**
* Protector for unprotected keys.
* This protector returns null for all [getEncryptor]/[getDecryptor] calls,
* no matter what the key-id is.
*
* As a consequence, this protector can only "unlock" keys which are not protected using a passphrase, and it will
* leave keys unprotected, should it be used to "protect" a key
* (e.g. in [org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor.changePassphraseFromOldPassphrase]).
*
* @return protector
*/
@JvmStatic
fun unprotectedKeys() = UnprotectedKeysProtector()
/**
* Use the provided map of key-ids and passphrases to unlock keys.
*
* @param passphraseMap map of key ids and their respective passphrases
* @return protector
*/
@JvmStatic
fun fromPassphraseMap(passphraseMap: Map<Long, Passphrase>): SecretKeyRingProtector =
CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null)
}
}