001// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.key.protection; 006 007import java.util.HashMap; 008import java.util.Map; 009import java.util.concurrent.ConcurrentHashMap; 010import javax.annotation.Nonnull; 011import javax.annotation.Nullable; 012 013import org.bouncycastle.openpgp.PGPException; 014import org.bouncycastle.openpgp.PGPSecretKey; 015import org.bouncycastle.openpgp.PGPSecretKeyRing; 016import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; 017import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; 018import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider; 019import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider; 020import org.pgpainless.util.Passphrase; 021 022/** 023 * Task of the {@link SecretKeyRingProtector} is to map encryptor/decryptor objects to key-ids. 024 * {@link PBESecretKeyEncryptor PBESecretKeyEncryptors}/{@link PBESecretKeyDecryptor PBESecretKeyDecryptors} are used 025 * to encrypt/decrypt secret keys using a passphrase. 026 * 027 * While it is easy to create an implementation of this interface that fits your needs, there are a bunch of 028 * implementations ready for use. 029 */ 030public interface SecretKeyRingProtector { 031 032 boolean hasPassphraseFor(Long keyId); 033 034 /** 035 * Return a decryptor for the key of id {@code keyId}. 036 * This method returns null if the key is unprotected. 037 * 038 * @param keyId id of the key 039 * @return decryptor for the key 040 */ 041 @Nullable PBESecretKeyDecryptor getDecryptor(Long keyId) throws PGPException; 042 043 /** 044 * Return an encryptor for the key of id {@code keyId}. 045 * This method returns null if the key is unprotected. 046 * 047 * @param keyId id of the key 048 * @return encryptor for the key 049 * @throws PGPException if the encryptor cannot be created for some reason 050 */ 051 @Nullable PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException; 052 053 /** 054 * Return a protector for secret keys. 055 * The protector maintains an in-memory cache of passphrases and can be extended with new passphrases 056 * at runtime. 057 * 058 * See {@link CachingSecretKeyRingProtector} for how to memorize/forget additional passphrases during runtime. 059 * 060 * @param missingPassphraseCallback callback that is used to provide missing passphrases. 061 * @return caching secret key protector 062 */ 063 static CachingSecretKeyRingProtector defaultSecretKeyRingProtector(SecretKeyPassphraseProvider missingPassphraseCallback) { 064 return new CachingSecretKeyRingProtector( 065 new HashMap<>(), 066 KeyRingProtectionSettings.secureDefaultSettings(), 067 missingPassphraseCallback); 068 } 069 070 /** 071 * Use the provided passphrase to lock/unlock all keys in the provided key ring. 072 * 073 * This protector will use the provided passphrase to lock/unlock all subkeys present in the provided keys object. 074 * For other keys that are not present in the ring, it will return null. 075 * 076 * @param passphrase passphrase 077 * @param keys key ring 078 * @return protector 079 * @deprecated use {@link #unlockEachKeyWith(Passphrase, PGPSecretKeyRing)} instead. 080 */ 081 @Deprecated 082 static SecretKeyRingProtector unlockAllKeysWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKeyRing keys) { 083 return unlockEachKeyWith(passphrase, keys); 084 } 085 086 /** 087 * Use the provided passphrase to lock/unlock all keys in the provided key ring. 088 * 089 * This protector will use the provided passphrase to lock/unlock all subkeys present in the provided keys object. 090 * For other keys that are not present in the ring, it will return null. 091 * 092 * @param passphrase passphrase 093 * @param keys key ring 094 * @return protector 095 */ 096 static SecretKeyRingProtector unlockEachKeyWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKeyRing keys) { 097 Map<Long, Passphrase> map = new ConcurrentHashMap<>(); 098 for (PGPSecretKey secretKey : keys) { 099 map.put(secretKey.getKeyID(), passphrase); 100 } 101 return fromPassphraseMap(map); 102 } 103 104 /** 105 * Use the provided passphrase to unlock any key. 106 * 107 * @param passphrase passphrase 108 * @return protector 109 */ 110 static SecretKeyRingProtector unlockAnyKeyWith(@Nonnull Passphrase passphrase) { 111 return new BaseSecretKeyRingProtector(new SolitaryPassphraseProvider(passphrase)); 112 } 113 114 /** 115 * Use the provided passphrase to lock/unlock only the provided (sub-)key. 116 * This protector will only return a non-null encryptor/decryptor based on the provided passphrase if 117 * {@link #getEncryptor(Long)}/{@link #getDecryptor(Long)} is getting called with the key-id of the provided key. 118 * 119 * Otherwise, this protector will always return null. 120 * 121 * @param passphrase passphrase 122 * @param key key to lock/unlock 123 * @return protector 124 */ 125 static SecretKeyRingProtector unlockSingleKeyWith(@Nonnull Passphrase passphrase, @Nonnull PGPSecretKey key) { 126 return PasswordBasedSecretKeyRingProtector.forKey(key, passphrase); 127 } 128 129 static SecretKeyRingProtector unlockSingleKeyWith(@Nonnull Passphrase passphrase, long keyId) { 130 return PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase); 131 } 132 133 /** 134 * Protector for unprotected keys. 135 * This protector returns null for all {@link #getEncryptor(Long)}/{@link #getDecryptor(Long)} calls, 136 * no matter what the key-id is. 137 * 138 * As a consequence, this protector can only "unlock" keys which are not protected using a passphrase, and it will 139 * leave keys unprotected, should it be used to "protect" a key 140 * (e.g. in {@link org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor#changePassphraseFromOldPassphrase(Passphrase)}). 141 * 142 * @return protector 143 */ 144 static SecretKeyRingProtector unprotectedKeys() { 145 return new UnprotectedKeysProtector(); 146 } 147 148 /** 149 * Use the provided map of key-ids and passphrases to unlock keys. 150 * 151 * @param passphraseMap map of key ids and their respective passphrases 152 * @return protector 153 */ 154 static SecretKeyRingProtector fromPassphraseMap(@Nonnull Map<Long, Passphrase> passphraseMap) { 155 return new CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null); 156 } 157}