From b909778857b3e0b7eb86ac26c818e5f25baaddbd Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sat, 17 Nov 2018 16:23:51 -0500 Subject: Make exceptions easier to translate to error codes Avoid throwing base Botan::Exception type, as it is difficult to determine what the error is in that case. Add Exception::error_code and Exception::error_type which allows (for error code) more information about the error and (for error type) allows knowing the error type without requiring a sequence of catches. See GH #1742 --- src/lib/utils/exceptn.h | 306 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 238 insertions(+), 68 deletions(-) (limited to 'src/lib/utils/exceptn.h') diff --git a/src/lib/utils/exceptn.h b/src/lib/utils/exceptn.h index dfe428d72..bfff85618 100644 --- a/src/lib/utils/exceptn.h +++ b/src/lib/utils/exceptn.h @@ -1,6 +1,6 @@ /* * Exceptions -* (C) 1999-2009 Jack Lloyd +* (C) 1999-2009,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -14,23 +14,105 @@ namespace Botan { +/** +* Different types of errors that might occur +*/ +enum class ErrorType { + /** Some unknown error */ + Unknown = 1, + /** An error while calling a system interface */ + SystemError, + /** An operation seems valid, but not supported by the current version */ + NotImplemented, + /** Memory allocation failure */ + OutOfMemory, + /** An internal error occurred */ + InternalError, + /** An I/O error occurred */ + IoError, + + /** Invalid object state */ + InvalidObjectState = 100, + /** A key was not set on an object when this is required */ + KeyNotSet, + /** The application provided an argument which is invalid */ + InvalidArgument, + /** A key with invalid length was provided */ + InvalidKeyLength, + /** A nonce with invalid length was provided */ + InvalidNonceLength, + /** An object type was requested but cannot be found */ + LookupError, + /** Encoding a message or datum failed */ + EncodingFailure, + /** Decoding a message or datum failed */ + DecodingFailure, + /** A TLS error (error_code will be the alert type) */ + TLSError, + /** An error during an HTTP operation */ + HttpError, + + /** An error when calling OpenSSL */ + OpenSSLError = 200, + /** An error when interacting with CommonCrypto API */ + CommonCryptoError, + /** An error when interacting with a PKCS11 device */ + Pkcs11Error, + /** An error when interacting with a TPM device */ + TPMError, + + /** An error when interacting with zlib */ + ZlibError = 300, + /** An error when interacting with bzip2 */ + Bzip2Error, + /** An error when interacting with lzma */ + LzmaError, + +}; + /** * Base class for all exceptions thrown by the library */ class BOTAN_PUBLIC_API(2,0) Exception : public std::exception { public: - Exception(const char* prefix, const std::string& msg); - explicit Exception(const std::string& msg); + /** + * Return a descriptive string which is hopefully comprehensible to + * a developer. It will likely not be useful for an end user. + * + * The string has no particular format, and the content of exception + * messages may change from release to release. Thus the main use of this + * function is for logging or debugging. + */ const char* what() const noexcept override { return m_msg.c_str(); } + + /** + * Return the "type" of error which occurred. + */ + virtual ErrorType error_type() const noexcept { return Botan::ErrorType::Unknown; } + + /** + * Return an error code associated with this exception, or otherwise 0. + * + * The domain of this error varies depending on the source, for example on + * POSIX systems it might be errno, while on a Windows system it might be + * the result of GetLastError or WSAGetLastError. For error_type() is + * OpenSSLError, it will (if nonzero) be an OpenSSL error code from + * ERR_get_error. + */ + virtual int error_code() const noexcept { return 0; } + protected: + explicit Exception(const std::string& msg); + Exception(const char* prefix, const std::string& msg); Exception(const std::string& msg, const std::exception& e); + private: std::string m_msg; }; /** -* An invalid argument +* An invalid argument was provided to an API call. */ class BOTAN_PUBLIC_API(2,0) Invalid_Argument : public Exception { @@ -40,95 +122,126 @@ class BOTAN_PUBLIC_API(2,0) Invalid_Argument : public Exception explicit Invalid_Argument(const std::string& msg, const std::string& where); Invalid_Argument(const std::string& msg, const std::exception& e); + + ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; } }; /** -* Unsupported_Argument Exception -* -* An argument that is invalid because it is not supported by Botan. -* It might or might not be valid in another context like a standard. +* An invalid key length was used */ -class BOTAN_PUBLIC_API(2,0) Unsupported_Argument final : public Invalid_Argument +class BOTAN_PUBLIC_API(2,0) Invalid_Key_Length final : public Invalid_Argument { public: - explicit Unsupported_Argument(const std::string& msg) : Invalid_Argument(msg) {} + Invalid_Key_Length(const std::string& name, size_t length); + ErrorType error_type() const noexcept override { return ErrorType::InvalidKeyLength; } }; /** -* Invalid_State Exception +* An invalid nonce length was used */ -class BOTAN_PUBLIC_API(2,0) Invalid_State : public Exception +class BOTAN_PUBLIC_API(2,0) Invalid_IV_Length final : public Invalid_Argument { public: - explicit Invalid_State(const std::string& err) : Exception(err) {} + Invalid_IV_Length(const std::string& mode, size_t bad_len); + ErrorType error_type() const noexcept override { return ErrorType::InvalidNonceLength; } }; -class BOTAN_PUBLIC_API(2,4) Key_Not_Set : public Invalid_State +/** +* Invalid_Algorithm_Name Exception +*/ +class BOTAN_PUBLIC_API(2,0) Invalid_Algorithm_Name final : public Invalid_Argument { public: - explicit Key_Not_Set(const std::string& algo); + explicit Invalid_Algorithm_Name(const std::string& name); }; /** -* Lookup_Error Exception +* Encoding_Error Exception +* +* This exception derives from Invalid_Argument for historical reasons, and it +* does not make any real sense for it to do so. In a future major release this +* exception type will derive directly from Exception instead. */ -class BOTAN_PUBLIC_API(2,0) Lookup_Error : public Exception +class BOTAN_PUBLIC_API(2,0) Encoding_Error final : public Invalid_Argument { public: - explicit Lookup_Error(const std::string& err) : Exception(err) {} + explicit Encoding_Error(const std::string& name); - Lookup_Error(const std::string& type, - const std::string& algo, - const std::string& provider); + ErrorType error_type() const noexcept override { return ErrorType::EncodingFailure; } }; /** -* Internal_Error Exception +* A decoding error occurred. +* +* This exception derives from Invalid_Argument for historical reasons, and it +* does not make any real sense for it to do so. In a future major release this +* exception type will derive directly from Exception instead. */ -class BOTAN_PUBLIC_API(2,0) Internal_Error : public Exception +class BOTAN_PUBLIC_API(2,0) Decoding_Error : public Invalid_Argument { public: - explicit Internal_Error(const std::string& err); + explicit Decoding_Error(const std::string& name); + + Decoding_Error(const std::string& name, const char* exception_message); + + Decoding_Error(const std::string& msg, const std::exception& e); + + ErrorType error_type() const noexcept override { return ErrorType::DecodingFailure; } }; /** -* Invalid_Key_Length Exception +* Invalid state was encountered. A request was made on an object while the +* object was in a state where the operation cannot be performed. */ -class BOTAN_PUBLIC_API(2,0) Invalid_Key_Length final : public Invalid_Argument +class BOTAN_PUBLIC_API(2,0) Invalid_State : public Exception { public: - Invalid_Key_Length(const std::string& name, size_t length); + explicit Invalid_State(const std::string& err) : Exception(err) {} + + ErrorType error_type() const noexcept override { return ErrorType::InvalidObjectState; } }; /** -* Invalid_IV_Length Exception +* A PRNG was called on to produce output while still unseeded */ -class BOTAN_PUBLIC_API(2,0) Invalid_IV_Length final : public Invalid_Argument +class BOTAN_PUBLIC_API(2,0) PRNG_Unseeded final : public Invalid_State { public: - Invalid_IV_Length(const std::string& mode, size_t bad_len); + explicit PRNG_Unseeded(const std::string& algo); }; /** -* PRNG_Unseeded Exception +* The key was not set on an object. This occurs with symmetric objects where +* an operation which requires the key is called prior to set_key being called. */ -class BOTAN_PUBLIC_API(2,0) PRNG_Unseeded final : public Invalid_State +class BOTAN_PUBLIC_API(2,4) Key_Not_Set : public Invalid_State { public: - explicit PRNG_Unseeded(const std::string& algo); + explicit Key_Not_Set(const std::string& algo); + + ErrorType error_type() const noexcept override { return ErrorType::KeyNotSet; } }; /** -* Policy_Violation Exception +* A request was made for some kind of object which could not be located */ -class BOTAN_PUBLIC_API(2,0) Policy_Violation final : public Invalid_State +class BOTAN_PUBLIC_API(2,0) Lookup_Error : public Exception { public: - BOTAN_DEPRECATED("deprecated") explicit Policy_Violation(const std::string& err); + explicit Lookup_Error(const std::string& err) : Exception(err) {} + + Lookup_Error(const std::string& type, + const std::string& algo, + const std::string& provider); + + ErrorType error_type() const noexcept override { return ErrorType::LookupError; } }; /** * Algorithm_Not_Found Exception +* +* @warning This exception type will be removed in the future. Instead +* just catch Lookup_Error. */ class BOTAN_PUBLIC_API(2,0) Algorithm_Not_Found final : public Lookup_Error { @@ -137,66 +250,98 @@ class BOTAN_PUBLIC_API(2,0) Algorithm_Not_Found final : public Lookup_Error }; /** -* No_Provider_Found Exception +* Provider_Not_Found is thrown when a specific provider was requested +* but that provider is not available. +* +* @warning This exception type will be removed in the future. Instead +* just catch Lookup_Error. */ -class BOTAN_PUBLIC_API(2,0) No_Provider_Found final : public Exception +class BOTAN_PUBLIC_API(2,0) Provider_Not_Found final : public Lookup_Error { public: - BOTAN_DEPRECATED("deprecated") explicit No_Provider_Found(const std::string& name); + Provider_Not_Found(const std::string& algo, const std::string& provider); }; /** -* Provider_Not_Found is thrown when a specific provider was requested -* but that provider is not available. +* An AEAD or MAC check detected a message modification */ -class BOTAN_PUBLIC_API(2,0) Provider_Not_Found final : public Lookup_Error +class BOTAN_PUBLIC_API(2,0) Integrity_Failure final : public Exception { public: - Provider_Not_Found(const std::string& algo, const std::string& provider); + explicit Integrity_Failure(const std::string& msg); + + ErrorType error_type() const noexcept override { return ErrorType::DecodingFailure; } }; /** -* Invalid_Algorithm_Name Exception +* An error occurred while operating on an IO stream */ -class BOTAN_PUBLIC_API(2,0) Invalid_Algorithm_Name final : public Invalid_Argument +class BOTAN_PUBLIC_API(2,0) Stream_IO_Error final : public Exception { public: - explicit Invalid_Algorithm_Name(const std::string& name); + explicit Stream_IO_Error(const std::string& err); + + ErrorType error_type() const noexcept override { return ErrorType::IoError; } }; /** -* Encoding_Error Exception +* System_Error +* +* This exception is thrown in the event of an error related to interacting +* with the operating system. +* +* This exception type also (optionally) captures an integer error code eg +* POSIX errno or Windows GetLastError. */ -class BOTAN_PUBLIC_API(2,0) Encoding_Error final : public Invalid_Argument +class BOTAN_PUBLIC_API(2,9) System_Error : public Exception { public: - explicit Encoding_Error(const std::string& name); + System_Error(const std::string& msg) : Exception(msg), m_error_code(0) {} + + System_Error(const std::string& msg, int err_code); + + ErrorType error_type() const noexcept override { return ErrorType::SystemError; } + + int error_code() const noexcept override { return m_error_code; } + + private: + int m_error_code; }; /** -* Decoding_Error Exception +* An internal error occurred. If observed, please file a bug. */ -class BOTAN_PUBLIC_API(2,0) Decoding_Error : public Invalid_Argument +class BOTAN_PUBLIC_API(2,0) Internal_Error : public Exception { public: - explicit Decoding_Error(const std::string& name); - - Decoding_Error(const std::string& name, const char* exception_message); + explicit Internal_Error(const std::string& err); - Decoding_Error(const std::string& msg, const std::exception& e); + ErrorType error_type() const noexcept override { return ErrorType::InternalError; } }; /** -* Integrity_Failure Exception +* Not Implemented Exception +* +* This is thrown in the situation where a requested operation is +* logically valid but is not implemented by this version of the library. */ -class BOTAN_PUBLIC_API(2,0) Integrity_Failure final : public Exception +class BOTAN_PUBLIC_API(2,0) Not_Implemented final : public Exception { public: - explicit Integrity_Failure(const std::string& msg); + explicit Not_Implemented(const std::string& err); + + ErrorType error_type() const noexcept override { return ErrorType::NotImplemented; } }; +/* + The following exception types are still in use for compatability reasons, + but are deprecated and will be removed in a future major release +*/ + /** -* Invalid_OID Exception +* An invalid OID string was used. +* +* This exception will be removed in a future major release. */ class BOTAN_PUBLIC_API(2,0) Invalid_OID final : public Decoding_Error { @@ -204,33 +349,58 @@ class BOTAN_PUBLIC_API(2,0) Invalid_OID final : public Decoding_Error explicit Invalid_OID(const std::string& oid); }; +/* + The following exception types are deprecated, no longer used, + and will be removed in a future major release +*/ + /** -* Stream_IO_Error Exception +* Self Test Failure Exception */ -class BOTAN_PUBLIC_API(2,0) Stream_IO_Error final : public Exception +class BOTAN_PUBLIC_API(2,0) Self_Test_Failure final : public Internal_Error { public: - explicit Stream_IO_Error(const std::string& err); + BOTAN_DEPRECATED("no longer used") explicit Self_Test_Failure(const std::string& err); }; /** -* Self Test Failure Exception +* No_Provider_Found Exception */ -class BOTAN_PUBLIC_API(2,0) Self_Test_Failure final : public Internal_Error +class BOTAN_PUBLIC_API(2,0) No_Provider_Found final : public Exception { public: - BOTAN_DEPRECATED("deprecated") explicit Self_Test_Failure(const std::string& err); + BOTAN_DEPRECATED("no longer used") explicit No_Provider_Found(const std::string& name); }; /** -* Not Implemented Exception +* Policy_Violation Exception */ -class BOTAN_PUBLIC_API(2,0) Not_Implemented final : public Exception +class BOTAN_PUBLIC_API(2,0) Policy_Violation final : public Invalid_State { public: - explicit Not_Implemented(const std::string& err); + BOTAN_DEPRECATED("no longer used") explicit Policy_Violation(const std::string& err); + }; + +/** +* Unsupported_Argument Exception +* +* An argument that is invalid because it is not supported by Botan. +* It might or might not be valid in another context like a standard. +* +* This exception is no longer used, instead Not_Implemented is thrown. +*/ +class BOTAN_PUBLIC_API(2,0) Unsupported_Argument final : public Invalid_Argument + { + public: + BOTAN_DEPRECATED("no longer used") explicit Unsupported_Argument(const std::string& msg) : Invalid_Argument(msg) {} }; +template +inline void do_throw_error(const char* file, int line, const char* func, Args... args) + { + throw E(file, line, func, args...); + } + } #endif -- cgit v1.2.3