diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java deleted file mode 100644 index 6664dea7..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.bcpg.KeyIdentifier; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.api.OpenPGPCertificate; -import org.pgpainless.algorithm.PublicKeyAlgorithm; -import org.pgpainless.key.OpenPgpFingerprint; -import org.pgpainless.util.DateUtil; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Date; - -public abstract class KeyException extends RuntimeException { - - private final OpenPgpFingerprint fingerprint; - - protected KeyException(@Nonnull String message, @Nonnull OpenPgpFingerprint fingerprint) { - super(message); - this.fingerprint = fingerprint; - } - - protected KeyException(@Nonnull String message, @Nonnull OpenPgpFingerprint fingerprint, @Nonnull Throwable underlying) { - super(message, underlying); - this.fingerprint = fingerprint; - } - - public OpenPgpFingerprint getFingerprint() { - return fingerprint; - } - - public static class ExpiredKeyException extends KeyException { - - public ExpiredKeyException(@Nonnull OpenPGPCertificate cert, @Nonnull Date expirationDate) { - this(OpenPgpFingerprint.of(cert), expirationDate); - } - - public ExpiredKeyException(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull Date expirationDate) { - super("Key " + fingerprint + " is expired. Expiration date: " + DateUtil.formatUTCDate(expirationDate), fingerprint); - } - } - - public static class RevokedKeyException extends KeyException { - - public RevokedKeyException(@Nonnull OpenPgpFingerprint fingerprint) { - super("Key " + fingerprint + " appears to be revoked.", fingerprint); - } - - public RevokedKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey key) { - super("Subkey " + key.getKeyIdentifier() + " appears to be revoked.", - OpenPgpFingerprint.of(key)); - } - - public RevokedKeyException(@Nonnull OpenPGPCertificate cert) { - super("Key or certificate " + cert.getKeyIdentifier() + " appears to be revoked.", - OpenPgpFingerprint.of(cert)); - } - } - - public static class UnacceptableEncryptionKeyException extends KeyException { - - public UnacceptableEncryptionKeyException(@Nonnull OpenPGPCertificate cert) { - this(OpenPgpFingerprint.of(cert)); - } - - public UnacceptableEncryptionKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey subkey) { - super("Subkey " + subkey.getKeyIdentifier() + " is not an acceptable encryption key.", - OpenPgpFingerprint.of(subkey)); - } - - public UnacceptableEncryptionKeyException(@Nonnull OpenPgpFingerprint fingerprint) { - super("Key " + fingerprint + " has no acceptable encryption key.", fingerprint); - } - - public UnacceptableEncryptionKeyException(@Nonnull PublicKeyAlgorithmPolicyException reason) { - super("Key " + reason.getFingerprint() + " has no acceptable encryption key.", reason.getFingerprint(), reason); - } - } - - public static class UnacceptableSigningKeyException extends KeyException { - - public UnacceptableSigningKeyException(OpenPGPCertificate certificate) { - this(OpenPgpFingerprint.of(certificate)); - } - - public UnacceptableSigningKeyException(OpenPGPCertificate.OpenPGPComponentKey subkey) { - this(OpenPgpFingerprint.of(subkey)); - } - - public UnacceptableSigningKeyException(@Nonnull OpenPgpFingerprint fingerprint) { - super("Key " + fingerprint + " has no acceptable signing key.", fingerprint); - } - - public UnacceptableSigningKeyException(@Nonnull PublicKeyAlgorithmPolicyException reason) { - super("Key " + reason.getFingerprint() + " has no acceptable signing key.", reason.getFingerprint(), reason); - } - } - - public static class UnacceptableThirdPartyCertificationKeyException extends KeyException { - - public UnacceptableThirdPartyCertificationKeyException(@Nonnull OpenPgpFingerprint fingerprint) { - super("Key " + fingerprint + " has no acceptable certification key.", fingerprint); - } - } - - public static class UnacceptableSelfSignatureException extends KeyException { - - public UnacceptableSelfSignatureException(@Nonnull OpenPGPCertificate cert) { - this(OpenPgpFingerprint.of(cert)); - } - - public UnacceptableSelfSignatureException(@Nonnull OpenPgpFingerprint fingerprint) { - super("Key " + fingerprint + " does not have a valid/acceptable signature to derive an expiration date from.", fingerprint); - } - } - - public static class MissingSecretKeyException extends KeyException { - - private final KeyIdentifier missingSecretKeyIdentifier; - - public MissingSecretKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey publicKey) { - this(OpenPgpFingerprint.of(publicKey.getCertificate()), publicKey.getKeyIdentifier()); - } - - public MissingSecretKeyException(@Nonnull OpenPgpFingerprint fingerprint, KeyIdentifier keyIdentifier) { - super("Key " + fingerprint + " does not contain a secret key for public key " + keyIdentifier, fingerprint); - this.missingSecretKeyIdentifier = keyIdentifier; - } - - @Deprecated - public MissingSecretKeyException(@Nonnull OpenPgpFingerprint fingerprint, long keyId) { - this(fingerprint, new KeyIdentifier(keyId)); - } - - public KeyIdentifier getMissingSecretKeyIdentifier() { - return missingSecretKeyIdentifier; - } - } - - public static class PublicKeyAlgorithmPolicyException extends KeyException { - - private final KeyIdentifier violatingSubkeyId; - - public PublicKeyAlgorithmPolicyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey subkey, - @Nonnull PublicKeyAlgorithm algorithm, - int bitSize) { - super("Subkey " + subkey.getKeyIdentifier() + " of key " + subkey.getCertificate().getKeyIdentifier() + - " is violating the Public Key Algorithm Policy:\n" + - algorithm + " of size " + bitSize + " is not acceptable.", OpenPgpFingerprint.of(subkey)); - this.violatingSubkeyId = subkey.getKeyIdentifier(); - } - - public PublicKeyAlgorithmPolicyException(@Nonnull OpenPgpFingerprint fingerprint, - long keyId, - @Nonnull PublicKeyAlgorithm algorithm, - int bitSize) { - super("Subkey " + Long.toHexString(keyId) + " of key " + fingerprint + " is violating the Public Key Algorithm Policy:\n" + - algorithm + " of size " + bitSize + " is not acceptable.", fingerprint); - this.violatingSubkeyId = new KeyIdentifier(keyId); - } - - public KeyIdentifier getViolatingSubkeyId() { - return violatingSubkeyId; - } - } - - public static class UnboundUserIdException extends KeyException { - - public UnboundUserIdException(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull String userId, - @Nullable PGPSignature userIdSignature, @Nullable PGPSignature userIdRevocation) { - super(errorMessage(fingerprint, userId, userIdSignature, userIdRevocation), fingerprint); - } - - private static String errorMessage(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull String userId, - @Nullable PGPSignature userIdSignature, @Nullable PGPSignature userIdRevocation) { - String errorMessage = "UserID '" + userId + "' is not valid for key " + fingerprint + ": "; - if (userIdSignature == null) { - return errorMessage + "Missing binding signature."; - } - if (userIdRevocation != null) { - return errorMessage + "UserID is revoked."; - } - return errorMessage + "Unacceptable binding signature."; - } - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyIntegrityException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/KeyIntegrityException.java deleted file mode 100644 index b7a87ab7..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyIntegrityException.java +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -/** - * This exception gets thrown, when the integrity of an OpenPGP key is broken. - * That could happen on accident, or during an active attack, so take this exception seriously. - */ -public class KeyIntegrityException extends AssertionError { - - public KeyIntegrityException() { - super("Key Integrity Exception"); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/MalformedOpenPgpMessageException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/MalformedOpenPgpMessageException.java deleted file mode 100644 index f98a4048..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/MalformedOpenPgpMessageException.java +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.pgpainless.decryption_verification.syntax_check.InputSymbol; -import org.pgpainless.decryption_verification.syntax_check.StackSymbol; -import org.pgpainless.decryption_verification.syntax_check.State; - -/** - * Exception that gets thrown if the OpenPGP message is malformed. - * Malformed messages are messages which do not follow the grammar specified in the RFC. - * - * @see RFC4880 §11.3. OpenPGP Messages - */ -public class MalformedOpenPgpMessageException extends RuntimeException { - - public MalformedOpenPgpMessageException(String message) { - super(message); - } - - public MalformedOpenPgpMessageException(State state, InputSymbol input, StackSymbol stackItem) { - this("There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack."); - } - - public MalformedOpenPgpMessageException(String s, MalformedOpenPgpMessageException e) { - super(s, e); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/MessageNotIntegrityProtectedException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/MessageNotIntegrityProtectedException.java deleted file mode 100644 index 1d8559e1..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/MessageNotIntegrityProtectedException.java +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.openpgp.PGPException; - -public class MessageNotIntegrityProtectedException extends PGPException { - - public MessageNotIntegrityProtectedException() { - super("Message is encrypted using a 'Symmetrically Encrypted Data' (SED) packet, which enables certain types of attacks. " + - "A 'Symmetrically Encrypted Integrity Protected' (SEIP) packet should be used instead."); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/MissingDecryptionMethodException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/MissingDecryptionMethodException.java deleted file mode 100644 index 0e856ba6..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/MissingDecryptionMethodException.java +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.openpgp.PGPException; - -/** - * Exception that is thrown when decryption fails due to a missing decryption key or decryption passphrase. - * This can happen when the user does not provide the right set of keys / the right password when decrypting - * a message. - */ -public class MissingDecryptionMethodException extends PGPException { - - public MissingDecryptionMethodException(String message) { - super(message); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/MissingPassphraseException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/MissingPassphraseException.java deleted file mode 100644 index 3f8e0799..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/MissingPassphraseException.java +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Set; - -import org.bouncycastle.openpgp.PGPException; -import org.pgpainless.key.SubkeyIdentifier; - -public class MissingPassphraseException extends PGPException { - - private final Set keyIds; - - public MissingPassphraseException(Set keyIds) { - super("Missing passphrase encountered for keys " + Arrays.toString(keyIds.toArray())); - this.keyIds = Collections.unmodifiableSet(keyIds); - } - - public Set getKeyIds() { - return keyIds; - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/ModificationDetectionException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/ModificationDetectionException.java deleted file mode 100644 index 5be1b359..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/ModificationDetectionException.java +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import java.io.IOException; - -/** - * Exception that gets thrown when the verification of a modification detection code failed. - */ -public class ModificationDetectionException extends IOException { - -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java deleted file mode 100644 index 2141ec5c..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import java.util.Map; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSignature; -import org.pgpainless.algorithm.SignatureType; - -public class SignatureValidationException extends PGPException { - - public SignatureValidationException(String message) { - super(message); - } - - public SignatureValidationException(String message, Exception underlying) { - super(message, underlying); - } - - public SignatureValidationException(String message, Map rejections) { - super(message + ": " + exceptionMapToString(rejections)); - } - - private static String exceptionMapToString(Map rejections) { - StringBuilder sb = new StringBuilder(); - sb.append(rejections.size()).append(" rejected signatures:\n"); - for (PGPSignature signature : rejections.keySet()) { - String typeString; - SignatureType type = SignatureType.fromCode(signature.getSignatureType()); - if (type == null) { - typeString = "0x" + Long.toHexString(signature.getSignatureType()); - } else { - typeString = type.toString(); - } - sb.append(typeString).append(' ') - .append(signature.getCreationTime()).append(": ") - .append(rejections.get(signature).getMessage()).append('\n'); - } - return sb.toString(); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/UnacceptableAlgorithmException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/UnacceptableAlgorithmException.java deleted file mode 100644 index aa3c8603..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/UnacceptableAlgorithmException.java +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.openpgp.PGPException; - -/** - * Exception that gets thrown if unacceptable algorithms are encountered. - */ -public class UnacceptableAlgorithmException extends PGPException { - - public UnacceptableAlgorithmException(String message) { - super(message); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java deleted file mode 100644 index 93d2e9c5..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.openpgp.PGPException; - -public class WrongConsumingMethodException extends PGPException { - - public WrongConsumingMethodException(String message) { - super(message); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/WrongPassphraseException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/WrongPassphraseException.java deleted file mode 100644 index d039ca6a..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/WrongPassphraseException.java +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.exception; - -import org.bouncycastle.bcpg.KeyIdentifier; -import org.bouncycastle.openpgp.PGPException; - -public class WrongPassphraseException extends PGPException { - - public WrongPassphraseException(String message) { - super(message); - } - - public WrongPassphraseException(long keyId, PGPException cause) { - this(new KeyIdentifier(keyId), cause); - } - - public WrongPassphraseException(KeyIdentifier keyIdentifier, PGPException cause) { - this("Wrong passphrase provided for key " + keyIdentifier, cause); - } - - public WrongPassphraseException(String message, PGPException cause) { - super(message, cause); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/exception/package-info.java deleted file mode 100644 index 01a786aa..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * Exceptions. - */ -package org.pgpainless.exception; diff --git a/pgpainless-core/src/main/java/org/pgpainless/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/package-info.java deleted file mode 100644 index 3573fcdc..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * PGPainless - Use OpenPGP Painlessly! - * - * @see org.pgpainless.core.org - */ -package org.pgpainless; diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyException.kt new file mode 100644 index 00000000..de9a7211 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyException.kt @@ -0,0 +1,203 @@ +package org.pgpainless.exception + +import java.util.* +import javax.annotation.Nonnull +import org.bouncycastle.bcpg.KeyIdentifier +import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.api.OpenPGPCertificate +import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey +import org.pgpainless.algorithm.PublicKeyAlgorithm +import org.pgpainless.key.OpenPgpFingerprint +import org.pgpainless.key.OpenPgpFingerprint.Companion.of +import org.pgpainless.util.DateUtil.Companion.formatUTCDate + +abstract class KeyException : RuntimeException { + + val fingerprint: OpenPgpFingerprint + + protected constructor(message: String, fingerprint: OpenPgpFingerprint) : super(message) { + this.fingerprint = fingerprint + } + + protected constructor( + message: String, + fingerprint: OpenPgpFingerprint, + underlying: Throwable + ) : super(message, underlying) { + this.fingerprint = fingerprint + } + + class ExpiredKeyException(fingerprint: OpenPgpFingerprint, expirationDate: Date) : + KeyException( + "Key $fingerprint is expired. Expiration date: ${formatUTCDate(expirationDate)}", + fingerprint, + ) { + + constructor(cert: OpenPGPCertificate, expirationDate: Date) : this(of(cert), expirationDate) + } + + class RevokedKeyException : KeyException { + constructor( + fingerprint: OpenPgpFingerprint + ) : super( + "Key $fingerprint appears to be revoked.", + fingerprint, + ) + + constructor( + componentKey: OpenPGPComponentKey + ) : super( + "Subkey ${componentKey.keyIdentifier} appears to be revoked.", + of(componentKey), + ) + + constructor( + cert: OpenPGPCertificate + ) : super( + "Key or certificate ${cert.keyIdentifier} appears to be revoked.", + of(cert), + ) + } + + class UnacceptableEncryptionKeyException : KeyException { + constructor(cert: OpenPGPCertificate) : this(of(cert)) + + constructor( + subkey: OpenPGPComponentKey + ) : super( + "Subkey ${subkey.keyIdentifier} is not an acceptable encryption key.", + of(subkey), + ) + + constructor( + fingerprint: OpenPgpFingerprint + ) : super("Key $fingerprint has no acceptable encryption key.", fingerprint) + + constructor( + reason: PublicKeyAlgorithmPolicyException + ) : super( + "Key ${reason.fingerprint} has no acceptable encryption key.", + reason.fingerprint, + reason) + } + + class UnacceptableSigningKeyException : KeyException { + constructor(cert: OpenPGPCertificate) : this(of(cert)) + + constructor(subkey: OpenPGPComponentKey) : this(of(subkey)) + + constructor( + fingerprint: OpenPgpFingerprint + ) : super("Key $fingerprint has no acceptable signing key.", fingerprint) + + constructor( + reason: KeyException.PublicKeyAlgorithmPolicyException + ) : super( + "Key ${reason.fingerprint} has no acceptable signing key.", reason.fingerprint, reason) + } + + class UnacceptableThirdPartyCertificationKeyException(fingerprint: OpenPgpFingerprint) : + KeyException("Key $fingerprint has no acceptable certification key.", fingerprint) {} + + class UnacceptableSelfSignatureException : KeyException { + constructor(cert: OpenPGPCertificate) : this(of(cert)) + + constructor( + fingerprint: OpenPgpFingerprint + ) : super( + "Key $fingerprint does not have a valid/acceptable signature to derive an expiration date from.", + fingerprint, + ) + } + + class MissingSecretKeyException : KeyException { + val missingSecretKeyIdentifier: KeyIdentifier + + constructor( + publicKey: OpenPGPComponentKey + ) : this( + of(publicKey.certificate), + publicKey.keyIdentifier, + ) + + constructor( + fingerprint: OpenPgpFingerprint, + keyIdentifier: KeyIdentifier + ) : super( + "Key $fingerprint does not contain a secret key for public key $keyIdentifier", + fingerprint, + ) { + missingSecretKeyIdentifier = keyIdentifier + } + + @Deprecated("Pass in a KeyIdentifier instead.") + constructor( + fingerprint: OpenPgpFingerprint, + keyId: Long + ) : this(fingerprint, KeyIdentifier(keyId)) + } + + class PublicKeyAlgorithmPolicyException : KeyException { + val violatingSubkeyId: KeyIdentifier + + constructor( + subkey: OpenPGPComponentKey, + algorithm: PublicKeyAlgorithm, + bitSize: Int + ) : super( + """Subkey ${subkey.keyIdentifier} of key ${subkey.certificate.keyIdentifier} is violating the Public Key Algorithm Policy: +$algorithm of size $bitSize is not acceptable.""", + of(subkey), + ) { + this.violatingSubkeyId = subkey.keyIdentifier + } + + constructor( + fingerprint: OpenPgpFingerprint, + keyId: Long, + algorithm: PublicKeyAlgorithm, + bitSize: Int + ) : super( + """Subkey ${java.lang.Long.toHexString(keyId)} of key $fingerprint is violating the Public Key Algorithm Policy: +$algorithm of size $bitSize is not acceptable.""", + fingerprint, + ) { + this.violatingSubkeyId = KeyIdentifier(keyId) + } + } + + class UnboundUserIdException( + fingerprint: OpenPgpFingerprint, + userId: String, + userIdSignature: PGPSignature?, + userIdRevocation: PGPSignature? + ) : + KeyException( + errorMessage( + fingerprint, + userId, + userIdSignature, + userIdRevocation, + ), + fingerprint, + ) { + + companion object { + private fun errorMessage( + @Nonnull fingerprint: OpenPgpFingerprint, + @Nonnull userId: String, + userIdSignature: PGPSignature?, + userIdRevocation: PGPSignature? + ): String { + val errorMessage = "UserID '$userId' is not valid for key $fingerprint: " + if (userIdSignature == null) { + return errorMessage + "Missing binding signature." + } + if (userIdRevocation != null) { + return errorMessage + "UserID is revoked." + } + return errorMessage + "Unacceptable binding signature." + } + } + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyIntegrityException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyIntegrityException.kt new file mode 100644 index 00000000..a4444020 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/KeyIntegrityException.kt @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import java.lang.AssertionError + +/** + * This exception gets thrown, when the integrity of an OpenPGP key is broken. That could happen on + * accident, or during an active attack, so take this exception seriously. + */ +class KeyIntegrityException : AssertionError("Key Integrity Exception") diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MalformedOpenPgpMessageException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MalformedOpenPgpMessageException.kt new file mode 100644 index 00000000..23d9ffdc --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MalformedOpenPgpMessageException.kt @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.pgpainless.decryption_verification.syntax_check.InputSymbol +import org.pgpainless.decryption_verification.syntax_check.StackSymbol +import org.pgpainless.decryption_verification.syntax_check.State + +/** + * Exception that gets thrown if the OpenPGP message is malformed. Malformed messages are messages + * which do not follow the grammar specified in the RFC. + * + * @see [RFC4880 §11.3. OpenPGP Messages](https://www.rfc-editor.org/rfc/rfc4880#section-11.3) + */ +class MalformedOpenPgpMessageException : RuntimeException { + constructor(message: String) : super(message) + + constructor(message: String, e: MalformedOpenPgpMessageException) : super(message, e) + + constructor( + state: State, + input: InputSymbol, + stackItem: StackSymbol? + ) : this( + "There is no legal transition from state '$state' for input '$input' when '${stackItem ?: "null"}' is on top of the stack.", + ) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MessageNotIntegrityProtectedException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MessageNotIntegrityProtectedException.kt new file mode 100644 index 00000000..df3b8eb8 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MessageNotIntegrityProtectedException.kt @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.openpgp.PGPException + +class MessageNotIntegrityProtectedException : + PGPException( + "Message is encrypted using a 'Symmetrically Encrypted Data' (SED) packet, which enables certain types of attacks. " + + "A 'Symmetrically Encrypted Integrity Protected' (SEIP) packet should be used instead.", + ) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingDecryptionMethodException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingDecryptionMethodException.kt new file mode 100644 index 00000000..1f1237f2 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingDecryptionMethodException.kt @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.openpgp.PGPException + +/** + * Exception that is thrown when decryption fails due to a missing decryption key or decryption + * passphrase. This can happen when the user does not provide the right set of keys / the right + * password when decrypting a message. + */ +class MissingDecryptionMethodException(message: String) : PGPException(message) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingPassphraseException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingPassphraseException.kt new file mode 100644 index 00000000..010f4fc7 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/MissingPassphraseException.kt @@ -0,0 +1,11 @@ +package org.pgpainless.exception + +import java.util.* +import org.bouncycastle.openpgp.PGPException +import org.pgpainless.key.SubkeyIdentifier + +class MissingPassphraseException(keyIds: Set) : + PGPException( + "Missing passphrase encountered for keys ${keyIds.toTypedArray().contentToString()}") { + val keyIds: Set = Collections.unmodifiableSet(keyIds) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/ModificationDetectionException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/ModificationDetectionException.kt new file mode 100644 index 00000000..09796f85 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/ModificationDetectionException.kt @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import java.io.IOException + +/** Exception that gets thrown when the verification of a modification detection code failed. */ +class ModificationDetectionException : IOException() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/SignatureValidationException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/SignatureValidationException.kt new file mode 100644 index 00000000..6cc6b32e --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/SignatureValidationException.kt @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPSignature +import org.pgpainless.algorithm.SignatureType + +class SignatureValidationException : PGPException { + + constructor(message: String?) : super(message) + + constructor(message: String?, underlying: Exception) : super(message, underlying) + + constructor( + message: String, + rejections: Map + ) : super("$message: ${exceptionMapToString(rejections)}") + + companion object { + @JvmStatic + private fun exceptionMapToString(rejections: Map): String = + buildString { + append(rejections.size).append(" rejected signatures:\n") + for (signature in rejections.keys) { + append(sigTypeToString(signature.signatureType)) + .append(' ') + .append(signature.creationTime) + .append(": ") + .append(rejections[signature]!!.message) + .append('\n') + } + } + + @JvmStatic + private fun sigTypeToString(type: Int): String = + SignatureType.fromCode(type)?.toString() + ?: "0x${java.lang.Long.toHexString(type.toLong())}" + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/UnacceptableAlgorithmException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/UnacceptableAlgorithmException.kt new file mode 100644 index 00000000..b2783cd6 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/UnacceptableAlgorithmException.kt @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.openpgp.PGPException + +/** Exception that gets thrown if unacceptable algorithms are encountered. */ +class UnacceptableAlgorithmException(message: String) : PGPException(message) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongConsumingMethodException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongConsumingMethodException.kt new file mode 100644 index 00000000..e1fe0842 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongConsumingMethodException.kt @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.openpgp.PGPException + +class WrongConsumingMethodException(message: String) : PGPException(message) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongPassphraseException.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongPassphraseException.kt new file mode 100644 index 00000000..4ac7cf68 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/exception/WrongPassphraseException.kt @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.exception + +import org.bouncycastle.bcpg.KeyIdentifier +import org.bouncycastle.openpgp.PGPException + +class WrongPassphraseException : PGPException { + + constructor(message: String) : super(message) + + constructor(message: String, cause: PGPException) : super(message, cause) + + @Deprecated("Pass in a KeyIdentifier instead.") + constructor(keyId: Long, cause: PGPException) : this(KeyIdentifier(keyId), cause) + + constructor( + keyIdentifier: KeyIdentifier, + cause: PGPException + ) : this("Wrong passphrase provided for key $keyIdentifier", cause) +}