2021-10-07 15:48:52 +02:00
|
|
|
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2018-07-18 18:23:06 +02:00
|
|
|
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;
|
2020-11-29 19:08:52 +01:00
|
|
|
import java.util.Map;
|
2021-01-22 20:03:20 +01:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2021-11-20 20:19:22 +01:00
|
|
|
import javax.annotation.Nonnull;
|
2018-07-31 20:09:16 +02:00
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
2018-06-27 12:33:25 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
2020-11-29 19:08:52 +01:00
|
|
|
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;
|
2020-11-29 19:08:52 +01:00
|
|
|
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
|
|
|
|
2021-09-15 16:33:03 +02:00
|
|
|
boolean hasPassphraseFor(Long keyId);
|
|
|
|
|
2018-06-27 12:33:25 +02:00
|
|
|
/**
|
|
|
|
* Return a decryptor for the key of id {@code keyId}.
|
2018-07-31 20:09:16 +02:00
|
|
|
* This method returns null if the key is unprotected.
|
2018-06-27 12:33:25 +02:00
|
|
|
*
|
|
|
|
* @param keyId id of the key
|
|
|
|
* @return decryptor for the key
|
|
|
|
*/
|
2020-12-27 01:56:18 +01:00
|
|
|
@Nullable PBESecretKeyDecryptor getDecryptor(Long keyId) throws PGPException;
|
2018-06-05 01:30:58 +02:00
|
|
|
|
2018-06-27 12:33:25 +02:00
|
|
|
/**
|
|
|
|
* Return an encryptor for the key of id {@code keyId}.
|
2018-07-31 20:09:16 +02:00
|
|
|
* This method returns null if the key is unprotected.
|
2018-06-27 12:33:25 +02:00
|
|
|
*
|
|
|
|
* @param keyId id of the key
|
|
|
|
* @return encryptor for the key
|
|
|
|
* @throws PGPException if the encryptor cannot be created for some reason
|
|
|
|
*/
|
2018-07-31 20:09:16 +02:00
|
|
|
@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-05-14 18:37:47 +02:00
|
|
|
/**
|
2021-11-20 20:19:22 +01:00
|
|
|
* Use the provided passphrase to lock/unlock all keys in the provided key ring.
|
2021-05-14 18:37:47 +02:00
|
|
|
*
|
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.
|
|
|
|
*
|
2021-05-14 18:37:47 +02:00
|
|
|
* @param passphrase passphrase
|
|
|
|
* @param keys key ring
|
|
|
|
* @return protector
|
2021-11-20 20:19:22 +01:00
|
|
|
* @deprecated use {@link #unlockEachKeyWith(Passphrase, PGPSecretKeyRing)} instead.
|
2022-04-02 17:12:12 +02:00
|
|
|
*
|
|
|
|
* TODO: Remove in 1.2.X
|
2021-05-14 18:37:47 +02:00
|
|
|
*/
|
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) {
|
2021-01-22 20:03:20 +01:00
|
|
|
Map<Long, Passphrase> map = new ConcurrentHashMap<>();
|
|
|
|
for (PGPSecretKey secretKey : keys) {
|
|
|
|
map.put(secretKey.getKeyID(), passphrase);
|
|
|
|
}
|
|
|
|
return fromPassphraseMap(map);
|
2020-11-29 19:08:52 +01:00
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:37:47 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
2021-05-14 18:37:47 +02:00
|
|
|
*
|
|
|
|
* @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) {
|
2020-11-29 19:08:52 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:37:47 +02:00
|
|
|
/**
|
|
|
|
* 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)}).
|
2021-05-14 18:37:47 +02:00
|
|
|
*
|
|
|
|
* @return protector
|
|
|
|
*/
|
2020-11-29 19:08:52 +01:00
|
|
|
static SecretKeyRingProtector unprotectedKeys() {
|
|
|
|
return new UnprotectedKeysProtector();
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:37:47 +02:00
|
|
|
/**
|
|
|
|
* 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);
|
2020-11-29 19:08:52 +01:00
|
|
|
}
|
2018-06-05 01:30:58 +02:00
|
|
|
}
|