diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/CallbackBasedKeyringProtector.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/CallbackBasedKeyringProtector.java deleted file mode 100644 index 9e37eb32..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/CallbackBasedKeyringProtector.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.key.protection; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.pgpainless.implementation.ImplementationFactory; -import org.pgpainless.util.Passphrase; - -public class CallbackBasedKeyringProtector implements SecretKeyRingProtector2 { - - private final Map passphraseCache = new ConcurrentHashMap<>(); - private final Callback callback; - - public CallbackBasedKeyringProtector(Callback callback) { - if (callback == null) { - throw new NullPointerException("Callback MUST NOT be null."); - } - this.callback = callback; - } - - @Override - public PBESecretKeyDecryptor getDecryptor(PGPSecretKey key) throws PGPException { - Passphrase passphrase = lookupPassphraseInCache(key); - if (passphrase == null) { - passphrase = callback.getPassphraseFor(key); - passphraseCache.put(key.getKeyID(), passphrase); - } - return ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase); - } - - @Override - public PBESecretKeyEncryptor getEncryptor(PGPSecretKey key) throws PGPException { - Passphrase passphrase = lookupPassphraseInCache(key); - if (passphrase == null) { - passphrase = callback.getPassphraseFor(key); - passphraseCache.put(key.getKeyID(), passphrase); - } - return ImplementationFactory.getInstance().getPBESecretKeyEncryptor(key, passphrase); - } - - private Passphrase lookupPassphraseInCache(PGPSecretKey key) { - return passphraseCache.get(key.getKeyID()); - } - - public interface Callback { - Passphrase getPassphraseFor(PGPSecretKey secretKey); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector.java index 522b11ff..92a35400 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector.java @@ -50,6 +50,13 @@ public interface SecretKeyRingProtector { */ @Nullable PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException; + /** + * Use the provided passphrase to lock/unlock all subkeys in the provided key ring. + * + * @param passphrase passphrase + * @param keys key ring + * @return protector + */ static SecretKeyRingProtector unlockAllKeysWith(Passphrase passphrase, PGPSecretKeyRing keys) { Map map = new ConcurrentHashMap<>(); for (PGPSecretKey secretKey : keys) { @@ -58,14 +65,32 @@ public interface SecretKeyRingProtector { return fromPassphraseMap(map); } + /** + * Use the provided passphrase to lock/unlock only the provided (sub-)key. + * + * @param passphrase passphrase + * @param key key to lock/unlock + * @return protector + */ static SecretKeyRingProtector unlockSingleKeyWith(Passphrase passphrase, PGPSecretKey key) { return PasswordBasedSecretKeyRingProtector.forKey(key, passphrase); } + /** + * Protector for unprotected keys. + * + * @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 + */ static SecretKeyRingProtector fromPassphraseMap(Map passphraseMap) { return new PassphraseMapKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector2.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector2.java deleted file mode 100644 index f215641a..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtector2.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.key.protection; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; - -public interface SecretKeyRingProtector2 { - - PBESecretKeyDecryptor getDecryptor(PGPSecretKey key) throws PGPException; - - PBESecretKeyEncryptor getEncryptor(PGPSecretKey key) throws PGPException; -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtectorAdapter.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtectorAdapter.java deleted file mode 100644 index b9e46896..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SecretKeyRingProtectorAdapter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 Paul Schaub. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pgpainless.key.protection; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; - -public interface SecretKeyRingProtectorAdapter extends SecretKeyRingProtector, SecretKeyRingProtector2 { - - @Override - default PBESecretKeyDecryptor getDecryptor(PGPSecretKey key) throws PGPException { - return getDecryptor(key.getKeyID()); - } - - @Override - default PBESecretKeyEncryptor getEncryptor(PGPSecretKey key) throws PGPException { - return getEncryptor(key.getKeyID()); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/UnlockSecretKey.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/UnlockSecretKey.java index 94f18a86..8d47396a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/UnlockSecretKey.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/UnlockSecretKey.java @@ -34,15 +34,6 @@ public class UnlockSecretKey { } } - public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector2 protector) throws WrongPassphraseException { - try { - PBESecretKeyDecryptor decryptor = protector.getDecryptor(secretKey); - return secretKey.extractPrivateKey(decryptor); - } catch (PGPException e) { - throw new WrongPassphraseException(secretKey.getKeyID(), e); - } - } - public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, PBESecretKeyDecryptor decryptor) throws WrongPassphraseException { try { return secretKey.extractPrivateKey(decryptor); diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java index 1b81ef93..342a4154 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.java @@ -25,6 +25,15 @@ import org.pgpainless.util.Passphrase; */ public interface SecretKeyPassphraseProvider { + /** + * Return a passphrase for the given secret key. + * If no record is found, return null. + * Note: In case of an unprotected secret key, this method must may not return null, but a {@link Passphrase} with + * a content of null. + * + * @param secretKey secret key + * @return passphrase or null, if no passphrase record is found. + */ @Nullable default Passphrase getPassphraseFor(PGPSecretKey secretKey) { return getPassphraseFor(secretKey.getKeyID()); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/protection/SecretKeyRingProtectorTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/protection/SecretKeyRingProtectorTest.java index ef1a74c1..9e04297b 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/protection/SecretKeyRingProtectorTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/protection/SecretKeyRingProtectorTest.java @@ -126,22 +126,4 @@ public class SecretKeyRingProtectorTest { assertEquals(Passphrase.emptyPassphrase(), protector.getPassphraseFor(1L)); assertEquals(Passphrase.fromPassword("missingP455w0rd"), protector.getPassphraseFor(3L)); } - - @ParameterizedTest - @MethodSource("org.pgpainless.util.TestUtil#provideImplementationFactories") - public void testCallbackBasedKeyRingProtector(ImplementationFactory implementationFactory) throws IOException, PGPException { - ImplementationFactory.setFactoryImplementation(implementationFactory); - SecretKeyRingProtector2 protector = new CallbackBasedKeyringProtector(new CallbackBasedKeyringProtector.Callback() { - @Override - public Passphrase getPassphraseFor(PGPSecretKey secretKey) { - return TestKeys.CRYPTIE_PASSPHRASE; - } - }); - - PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); - for (PGPSecretKey secretKey : secretKeys) { - UnlockSecretKey.unlockSecretKey(secretKey, protector); - assertNotNull(protector.getEncryptor(secretKey)); - } - } }