// SPDX-FileCopyrightText: 2022 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 package org.pgpainless.sop; import java.util.HashSet; import java.util.Set; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.info.KeyInfo; import org.pgpainless.key.protection.CachingSecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.util.Passphrase; import javax.annotation.Nullable; /** * Implementation of the {@link SecretKeyRingProtector} which can be handed passphrases and keys separately, * and which then matches up passphrases and keys when needed. */ public class MatchMakingSecretKeyRingProtector implements SecretKeyRingProtector { private final Set passphrases = new HashSet<>(); private final Set keys = new HashSet<>(); private final CachingSecretKeyRingProtector protector = new CachingSecretKeyRingProtector(); /** * Add a single passphrase to the protector. * * @param passphrase passphrase */ public void addPassphrase(Passphrase passphrase) { if (passphrase.isEmpty()) { return; } if (!passphrases.add(passphrase)) { return; } for (PGPSecretKeyRing key : keys) { for (PGPSecretKey subkey : key) { if (protector.hasPassphrase(subkey.getKeyID())) { continue; } testPassphrase(passphrase, subkey); } } } /** * Add a single {@link PGPSecretKeyRing} to the protector. * * @param key secret keys */ public void addSecretKey(PGPSecretKeyRing key) { if (!keys.add(key)) { return; } for (PGPSecretKey subkey : key) { if (KeyInfo.isDecrypted(subkey)) { protector.addPassphrase(subkey.getKeyID(), Passphrase.emptyPassphrase()); } else { for (Passphrase passphrase : passphrases) { testPassphrase(passphrase, subkey); } } } } private void testPassphrase(Passphrase passphrase, PGPSecretKey subkey) { try { PBESecretKeyDecryptor decryptor = ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase); UnlockSecretKey.unlockSecretKey(subkey, decryptor); protector.addPassphrase(subkey.getKeyID(), passphrase); } catch (PGPException e) { // wrong password } } @Override public boolean hasPassphraseFor(long keyId) { return protector.hasPassphrase(keyId); } @Nullable @Override public PBESecretKeyDecryptor getDecryptor(long keyId) throws PGPException { return protector.getDecryptor(keyId); } @Nullable @Override public PBESecretKeyEncryptor getEncryptor(long keyId) throws PGPException { return protector.getEncryptor(keyId); } /** * Clear all known passphrases from the protector. */ public void clear() { for (Passphrase passphrase : passphrases) { passphrase.clear(); } for (PGPSecretKeyRing key : keys) { protector.forgetPassphrase(key); } } }