mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 11:27:57 +01:00
Get rid of redundant SecretKeyRingProtector implementations.
This commit is contained in:
parent
8313895f26
commit
95121e2a55
7 changed files with 34 additions and 156 deletions
|
@ -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<Long, Passphrase> 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);
|
||||
}
|
||||
}
|
|
@ -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<Long, Passphrase> 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<Long, Passphrase> passphraseMap) {
|
||||
return new PassphraseMapKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), 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;
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue