diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 7c577600..71b43744 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -739,7 +739,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { @Override public WithPassphrase withSecureDefaultSettings() { - return withCustomSettings(KeyRingProtectionSettings.secureDefaultSettings()); + return withCustomSettings(KeyRingProtectionSettings.saltedAndIterated()); } @Override diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java index f09f25f3..ab6344f5 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java @@ -536,7 +536,7 @@ public interface SecretKeyRingEditorInterface { */ default WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase( @Nullable Passphrase oldPassphrase) { - return changePassphraseFromOldPassphrase(oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()); + return changePassphraseFromOldPassphrase(oldPassphrase, KeyRingProtectionSettings.saltedAndIterated()); } /** @@ -563,7 +563,7 @@ public interface SecretKeyRingEditorInterface { default WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase( @Nonnull Long keyId, @Nullable Passphrase oldPassphrase) { - return changeSubKeyPassphraseFromOldPassphrase(keyId, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()); + return changeSubKeyPassphraseFromOldPassphrase(keyId, oldPassphrase, KeyRingProtectionSettings.saltedAndIterated()); } WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase( diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/Argon2AEAD.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/Argon2AEAD.java new file mode 100644 index 00000000..fabef5e9 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/Argon2AEAD.java @@ -0,0 +1,31 @@ +package org.pgpainless.key.protection; + +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.pgpainless.algorithm.AEADAlgorithm; +import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.util.Passphrase; + +public class Argon2AEAD implements KeyRingProtectionSettings { + + private final S2K.Argon2Params parameters; + private final SymmetricKeyAlgorithm encryptionAlgorithm; + private final AEADAlgorithm aeadAlgorithm; + + public Argon2AEAD() { + this(S2K.Argon2Params.universallyRecommendedParameters(), SymmetricKeyAlgorithm.AES_256, AEADAlgorithm.OCB); + } + + public Argon2AEAD(S2K.Argon2Params parameters, SymmetricKeyAlgorithm encryptionAlgorithm, AEADAlgorithm aeadAlgorithm) { + this.parameters = parameters; + this.encryptionAlgorithm = encryptionAlgorithm; + this.aeadAlgorithm = aeadAlgorithm; + } + + @Override + public PBESecretKeyEncryptor getEncryptor(Passphrase passphrase) throws PGPException { + // TODO: Implement. + return null; + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/BaseSecretKeyRingProtector.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/BaseSecretKeyRingProtector.java index 1a31d0e8..cf97f193 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/BaseSecretKeyRingProtector.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/BaseSecretKeyRingProtector.java @@ -14,7 +14,7 @@ import org.pgpainless.util.Passphrase; import javax.annotation.Nullable; /** - * Basic {@link SecretKeyRingProtector} implementation that respects the users {@link KeyRingProtectionSettings} when + * Basic {@link SecretKeyRingProtector} implementation that respects the users {@link SaltedAndIteratedS2K} when * encrypting keys. */ public class BaseSecretKeyRingProtector implements SecretKeyRingProtector { @@ -24,16 +24,16 @@ public class BaseSecretKeyRingProtector implements SecretKeyRingProtector { /** * Constructor that uses the given {@link SecretKeyPassphraseProvider} to retrieve passphrases and PGPainless' - * default {@link KeyRingProtectionSettings}. + * default {@link SaltedAndIteratedS2K}. * * @param passphraseProvider provider for passphrases */ public BaseSecretKeyRingProtector(SecretKeyPassphraseProvider passphraseProvider) { - this(passphraseProvider, KeyRingProtectionSettings.secureDefaultSettings()); + this(passphraseProvider, KeyRingProtectionSettings.saltedAndIterated()); } /** - * Constructor that uses the given {@link SecretKeyPassphraseProvider} and {@link KeyRingProtectionSettings}. + * Constructor that uses the given {@link SecretKeyPassphraseProvider} and {@link SaltedAndIteratedS2K}. * * @param passphraseProvider provider for passphrases * @param protectionSettings protection settings @@ -61,10 +61,6 @@ public class BaseSecretKeyRingProtector implements SecretKeyRingProtector { public PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException { Passphrase passphrase = passphraseProvider.getPassphraseFor(keyId); return passphrase == null || passphrase.isEmpty() ? null : - ImplementationFactory.getInstance().getPBESecretKeyEncryptor( - protectionSettings.getEncryptionAlgorithm(), - protectionSettings.getHashAlgorithm(), - protectionSettings.getS2kCount(), - passphrase); + protectionSettings.getEncryptor(passphrase); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/CachingSecretKeyRingProtector.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/CachingSecretKeyRingProtector.java index 8cdb3efb..ffaf588b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/CachingSecretKeyRingProtector.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/CachingSecretKeyRingProtector.java @@ -40,7 +40,7 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se public CachingSecretKeyRingProtector(@Nullable SecretKeyPassphraseProvider missingPassphraseCallback) { this( new HashMap<>(), - KeyRingProtectionSettings.secureDefaultSettings(), + KeyRingProtectionSettings.saltedAndIterated(), missingPassphraseCallback ); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/KeyRingProtectionSettings.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/KeyRingProtectionSettings.java index a93534ab..75ba95b8 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/KeyRingProtectionSettings.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/KeyRingProtectionSettings.java @@ -1,62 +1,12 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - package org.pgpainless.key.protection; -import javax.annotation.Nonnull; - +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.util.Passphrase; -/** - * Secret key protection settings for iterated and salted S2K. - */ -public class KeyRingProtectionSettings { - - private final SymmetricKeyAlgorithm encryptionAlgorithm; - private final HashAlgorithm hashAlgorithm; - private final int s2kCount; - - /** - * Create a {@link KeyRingProtectionSettings} object using the given encryption algorithm, SHA1 and - * 65536 iterations. - * - * @param encryptionAlgorithm encryption algorithm - */ - public KeyRingProtectionSettings(@Nonnull SymmetricKeyAlgorithm encryptionAlgorithm) { - this(encryptionAlgorithm, HashAlgorithm.SHA1, 0x60); // Same s2kCount (encoded) as used in BC. - } - - /** - * Constructor for custom salted and iterated S2K protection settings. - * The salt gets randomly chosen by the library each time. - * - * Note, that the s2kCount is the already encoded single-octet number. - * - * @see Encoding Formula - * - * @param encryptionAlgorithm encryption algorithm - * @param hashAlgorithm hash algorithm - * @param s2kCount encoded s2k iteration count - */ - public KeyRingProtectionSettings(@Nonnull SymmetricKeyAlgorithm encryptionAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, int s2kCount) { - this.encryptionAlgorithm = validateEncryptionAlgorithm(encryptionAlgorithm); - this.hashAlgorithm = hashAlgorithm; - if (s2kCount < 1) { - throw new IllegalArgumentException("s2kCount cannot be less than 1."); - } - this.s2kCount = s2kCount; - } - - private static SymmetricKeyAlgorithm validateEncryptionAlgorithm(SymmetricKeyAlgorithm encryptionAlgorithm) { - switch (encryptionAlgorithm) { - case NULL: - throw new IllegalArgumentException("Unencrypted is not allowed here!"); - default: - return encryptionAlgorithm; - } - } +public interface KeyRingProtectionSettings { /** * Secure default settings using {@link SymmetricKeyAlgorithm#AES_256}, {@link HashAlgorithm#SHA256} @@ -64,36 +14,19 @@ public class KeyRingProtectionSettings { * * @return secure protection settings */ - public static KeyRingProtectionSettings secureDefaultSettings() { - return new KeyRingProtectionSettings(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA256, 0x60); + static SaltedAndIteratedS2K saltedAndIterated() { + return new SaltedAndIteratedS2K(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA256, 0x60); + } + + static KeyRingProtectionSettings argon2() { + return new Argon2AEAD(); } /** - * Return the encryption algorithm. + * Return an {@link PBESecretKeyEncryptor} instance using these protection settings. * - * @return encryption algorithm + * @param passphrase passphrase + * @return encryptor */ - public @Nonnull SymmetricKeyAlgorithm getEncryptionAlgorithm() { - return encryptionAlgorithm; - } - - /** - * Return the hash algorithm. - * - * @return hash algorithm - */ - public @Nonnull HashAlgorithm getHashAlgorithm() { - return hashAlgorithm; - } - - /** - * Return the (encoded!) s2k iteration count. - * - * @see Encoding Formula - * - * @return encoded s2k count - */ - public int getS2kCount() { - return s2kCount; - } + PBESecretKeyEncryptor getEncryptor(Passphrase passphrase) throws PGPException; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.java index c5745068..adeebc77 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.java @@ -16,7 +16,7 @@ import org.pgpainless.util.Passphrase; /** * Provides {@link PBESecretKeyDecryptor} and {@link PBESecretKeyEncryptor} objects while getting the passphrases - * from a {@link SecretKeyPassphraseProvider} and using settings from an {@link KeyRingProtectionSettings}. + * from a {@link SecretKeyPassphraseProvider} and using settings from an {@link SaltedAndIteratedS2K}. */ public class PasswordBasedSecretKeyRingProtector extends BaseSecretKeyRingProtector { diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/protection/SaltedAndIteratedS2K.java b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SaltedAndIteratedS2K.java new file mode 100644 index 00000000..55304018 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/protection/SaltedAndIteratedS2K.java @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2018 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.protection; + +import javax.annotation.Nonnull; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.pgpainless.algorithm.HashAlgorithm; +import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.implementation.ImplementationFactory; +import org.pgpainless.util.Passphrase; + +/** + * Secret key protection settings for iterated and salted S2K. + */ +public class SaltedAndIteratedS2K implements KeyRingProtectionSettings { + + private final SymmetricKeyAlgorithm encryptionAlgorithm; + private final HashAlgorithm hashAlgorithm; + private final int s2kCount; + + /** + * Create a {@link SaltedAndIteratedS2K} object using the given encryption algorithm, SHA1 and + * 65536 iterations. + * + * @param encryptionAlgorithm encryption algorithm + */ + public SaltedAndIteratedS2K(@Nonnull SymmetricKeyAlgorithm encryptionAlgorithm) { + this(encryptionAlgorithm, HashAlgorithm.SHA1, 0x60); // Same s2kCount (encoded) as used in BC. + } + + /** + * Constructor for custom salted and iterated S2K protection settings. + * The salt gets randomly chosen by the library each time. + * + * Note, that the s2kCount is the already encoded single-octet number. + * + * @see Encoding Formula + * + * @param encryptionAlgorithm encryption algorithm + * @param hashAlgorithm hash algorithm + * @param s2kCount encoded s2k iteration count + */ + public SaltedAndIteratedS2K(@Nonnull SymmetricKeyAlgorithm encryptionAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, int s2kCount) { + this.encryptionAlgorithm = validateEncryptionAlgorithm(encryptionAlgorithm); + this.hashAlgorithm = hashAlgorithm; + if (s2kCount < 1) { + throw new IllegalArgumentException("s2kCount cannot be less than 1."); + } + this.s2kCount = s2kCount; + } + + private static SymmetricKeyAlgorithm validateEncryptionAlgorithm(SymmetricKeyAlgorithm encryptionAlgorithm) { + switch (encryptionAlgorithm) { + case NULL: + throw new IllegalArgumentException("Unencrypted is not allowed here!"); + default: + return encryptionAlgorithm; + } + } + + /** + * Return the encryption algorithm. + * + * @return encryption algorithm + */ + public @Nonnull SymmetricKeyAlgorithm getEncryptionAlgorithm() { + return encryptionAlgorithm; + } + + /** + * Return the hash algorithm. + * + * @return hash algorithm + */ + public @Nonnull HashAlgorithm getHashAlgorithm() { + return hashAlgorithm; + } + + /** + * Return the (encoded!) s2k iteration count. + * + * @see Encoding Formula + * + * @return encoded s2k count + */ + public int getS2kCount() { + return s2kCount; + } + + @Override + public PBESecretKeyEncryptor getEncryptor(Passphrase passphrase) + throws PGPException { + return ImplementationFactory.getInstance().getPBESecretKeyEncryptor( + getEncryptionAlgorithm(), + getHashAlgorithm(), + getS2kCount(), + passphrase); + } +} 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 d7ed5c85..0670a55f 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 @@ -72,7 +72,7 @@ public interface SecretKeyRingProtector { static CachingSecretKeyRingProtector defaultSecretKeyRingProtector(SecretKeyPassphraseProvider missingPassphraseCallback) { return new CachingSecretKeyRingProtector( new HashMap<>(), - KeyRingProtectionSettings.secureDefaultSettings(), + KeyRingProtectionSettings.saltedAndIterated(), missingPassphraseCallback); } @@ -163,6 +163,6 @@ public interface SecretKeyRingProtector { * @return protector */ static SecretKeyRingProtector fromPassphraseMap(@Nonnull Map passphraseMap) { - return new CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null); + return new CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.saltedAndIterated(), null); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java index a0ea6984..5438af08 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/ChangeSecretKeyRingPassphraseTest.java @@ -32,8 +32,8 @@ import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.protection.KeyRingProtectionSettings; import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; -import org.pgpainless.util.TestAllImplementations; import org.pgpainless.util.Passphrase; +import org.pgpainless.util.TestAllImplementations; public class ChangeSecretKeyRingPassphraseTest { @@ -54,7 +54,7 @@ public class ChangeSecretKeyRingPassphraseTest { PGPSecretKeyRing changedPassphraseKeyRing = secretKeys; - assertEquals(KeyRingProtectionSettings.secureDefaultSettings().getEncryptionAlgorithm().getAlgorithmId(), + assertEquals(KeyRingProtectionSettings.saltedAndIterated().getEncryptionAlgorithm().getAlgorithmId(), changedPassphraseKeyRing.getSecretKey().getKeyEncryptionAlgorithm()); assertThrows(PGPException.class, () -> diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/protection/InvalidProtectionSettingsTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/protection/InvalidProtectionSettingsTest.java index b0746398..fe5852a9 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/protection/InvalidProtectionSettingsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/protection/InvalidProtectionSettingsTest.java @@ -15,6 +15,6 @@ public class InvalidProtectionSettingsTest { @Test public void unencryptedKeyRingProtectionSettingsThrows() { assertThrows(IllegalArgumentException.class, () -> - new KeyRingProtectionSettings(SymmetricKeyAlgorithm.NULL, HashAlgorithm.SHA256, 0x60)); + new SaltedAndIteratedS2K(SymmetricKeyAlgorithm.NULL, HashAlgorithm.SHA256, 0x60)); } } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/protection/PassphraseProtectedKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/protection/PassphraseProtectedKeyTest.java index 45aaf20f..0107ca26 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/protection/PassphraseProtectedKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/protection/PassphraseProtectedKeyTest.java @@ -27,7 +27,7 @@ public class PassphraseProtectedKeyTest { * Protector that holds only the password of cryptie. */ private final PasswordBasedSecretKeyRingProtector protector = new PasswordBasedSecretKeyRingProtector( - KeyRingProtectionSettings.secureDefaultSettings(), + KeyRingProtectionSettings.saltedAndIterated(), new SecretKeyPassphraseProvider() { @Nullable @Override 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 a5030f74..c1d21f37 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 @@ -106,7 +106,7 @@ public class SecretKeyRingProtectorTest { Map passphraseMap = new ConcurrentHashMap<>(); passphraseMap.put(1L, Passphrase.emptyPassphrase()); CachingSecretKeyRingProtector protector = new CachingSecretKeyRingProtector(passphraseMap, - KeyRingProtectionSettings.secureDefaultSettings(), new SecretKeyPassphraseProvider() { + KeyRingProtectionSettings.saltedAndIterated(), new SecretKeyPassphraseProvider() { @Override public Passphrase getPassphraseFor(Long keyId) { return Passphrase.fromPassword("missingP455w0rd"); diff --git a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java index d3c503ab..ede8c699 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/symmetric_encryption/SymmetricEncryptionTest.java @@ -78,7 +78,7 @@ public class SymmetricEncryptionTest { // Test public key decryption PGPSecretKeyRingCollection decryptionKeys = TestKeys.getCryptieSecretKeyRingCollection(); SecretKeyRingProtector protector = new PasswordBasedSecretKeyRingProtector( - KeyRingProtectionSettings.secureDefaultSettings(), + KeyRingProtectionSettings.saltedAndIterated(), new SolitaryPassphraseProvider(Passphrase.fromPassword(TestKeys.CRYPTIE_PASSWORD))); decryptor = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertext))