pgpainless/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector.java

169 lines
6.6 KiB
Java
Raw Normal View History

2021-10-07 15:48:52 +02:00
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.protection;
2018-06-05 01:30:58 +02:00
2021-05-14 18:55:26 +02:00
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
2021-11-20 20:19:22 +01:00
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
2018-06-05 01:30:58 +02:00
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
2021-05-14 18:55:26 +02:00
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider;
2021-11-20 20:19:22 +01:00
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
import org.pgpainless.util.Passphrase;
2018-06-05 01:30:58 +02:00
2020-12-27 01:56:18 +01:00
/**
2021-06-24 13:57:53 +02:00
* Task of the {@link SecretKeyRingProtector} is to map encryptor/decryptor objects to key-ids.
* {@link PBESecretKeyEncryptor PBESecretKeyEncryptors}/{@link PBESecretKeyDecryptor PBESecretKeyDecryptors} 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.
2020-12-27 01:56:18 +01:00
*/
2018-06-07 18:12:13 +02:00
public interface SecretKeyRingProtector {
2018-06-05 01:30:58 +02:00
2023-01-16 20:15:57 +01:00
/**
* 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
*/
boolean hasPassphraseFor(Long keyId);
/**
* Return a decryptor for the key of id {@code keyId}.
* This method returns null if the key is unprotected.
*
* @param keyId id of the key
* @return decryptor for the key
2022-04-02 18:56:05 +02:00
*
* @throws PGPException if the decryptor cannot be created for some reason
*/
2020-12-27 01:56:18 +01:00
@Nullable PBESecretKeyDecryptor getDecryptor(Long keyId) throws PGPException;
2018-06-05 01:30:58 +02:00
/**
* Return an encryptor for the key of id {@code keyId}.
* This method returns null if the key is unprotected.
*
* @param keyId id of the key
* @return encryptor for the key
2022-04-02 18:56:05 +02:00
*
* @throws PGPException if the encryptor cannot be created for some reason
*/
@Nullable PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException;
2018-06-05 01:30:58 +02:00
2021-05-14 18:55:26 +02:00
/**
* Return a protector for secret keys.
* The protector maintains an in-memory cache of passphrases and can be extended with new passphrases
* at runtime.
*
2021-06-24 13:57:53 +02:00
* See {@link CachingSecretKeyRingProtector} for how to memorize/forget additional passphrases during runtime.
*
2021-05-14 18:55:26 +02:00
* @param missingPassphraseCallback callback that is used to provide missing passphrases.
* @return caching secret key protector
*/
static CachingSecretKeyRingProtector defaultSecretKeyRingProtector(SecretKeyPassphraseProvider missingPassphraseCallback) {
return new CachingSecretKeyRingProtector(
new HashMap<>(),
KeyRingProtectionSettings.secureDefaultSettings(),
missingPassphraseCallback);
}
/**
2021-11-20 20:19:22 +01:00
* Use the provided passphrase to lock/unlock all keys in the provided key ring.
*
2021-06-24 13:57:53 +02:00
* 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
2021-11-20 20:19:22 +01:00
* @deprecated use {@link #unlockEachKeyWith(Passphrase, PGPSecretKeyRing)} instead.
*
* TODO: Remove in 1.2.X
*/
2021-11-20 20:19:22 +01:00
@Deprecated
static SecretKeyRingProtector unlockAllKeysWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKeyRing keys) {
return unlockEachKeyWith(passphrase, keys);
}
/**
* 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
*/
static SecretKeyRingProtector unlockEachKeyWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKeyRing keys) {
Map<Long, Passphrase> map = new ConcurrentHashMap<>();
for (PGPSecretKey secretKey : keys) {
map.put(secretKey.getKeyID(), passphrase);
}
return fromPassphraseMap(map);
}
2021-11-20 20:19:22 +01:00
/**
* Use the provided passphrase to unlock any key.
*
* @param passphrase passphrase
* @return protector
*/
static SecretKeyRingProtector unlockAnyKeyWith(@Nonnull Passphrase passphrase) {
return new BaseSecretKeyRingProtector(new SolitaryPassphraseProvider(passphrase));
}
/**
* Use the provided passphrase to lock/unlock only the provided (sub-)key.
2021-06-24 13:57:53 +02:00
* This protector will only return a non-null encryptor/decryptor based on the provided passphrase if
* {@link #getEncryptor(Long)}/{@link #getDecryptor(Long)} is getting called with the key-id of the provided key.
*
2021-12-28 13:53:25 +01:00
* Otherwise, this protector will always return null.
*
* @param passphrase passphrase
* @param key key to lock/unlock
* @return protector
*/
2021-11-20 20:19:22 +01:00
static SecretKeyRingProtector unlockSingleKeyWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKey key) {
return PasswordBasedSecretKeyRingProtector.forKey(key, passphrase);
}
2021-11-20 20:19:22 +01:00
static SecretKeyRingProtector unlockSingleKeyWith(@Nonnull Passphrase passphrase, long keyId) {
2021-11-08 22:45:08 +01:00
return PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase);
}
/**
* Protector for unprotected keys.
2021-06-24 13:57:53 +02:00
* This protector returns null for all {@link #getEncryptor(Long)}/{@link #getDecryptor(Long)} 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
2021-12-28 13:53:25 +01:00
* (e.g. in {@link org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor#changePassphraseFromOldPassphrase(Passphrase)}).
*
* @return protector
*/
static SecretKeyRingProtector unprotectedKeys() {
return new 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
*/
2021-11-20 20:19:22 +01:00
static SecretKeyRingProtector fromPassphraseMap(@Nonnull Map<Long, Passphrase> passphraseMap) {
2021-05-14 18:55:26 +02:00
return new CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null);
}
2018-06-05 01:30:58 +02:00
}