diff --git a/doc/Basic.md b/doc/Basic.md index 3972487..9527310 100644 --- a/doc/Basic.md +++ b/doc/Basic.md @@ -2,24 +2,28 @@ This is the Basic Halite API: - * Encryption +* Encryption * Symmetric - * `Symmetric\Crypto::encrypt`(`HiddenString`, [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md), `bool?`): `string` - * `Symmetric\Crypto::decrypt`(`string`, [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md), `bool?`): `HiddenString` + * `Symmetric\Crypto::encrypt`([`HiddenString`](Classes/HiddenString.md), [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md)): `string` + * `Symmetric\Crypto::encryptWithAD`([`HiddenString`](Classes/HiddenString.md), [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md), `string`): `string` + * `Symmetric\Crypto::decrypt`(`string`, [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md)): [`HiddenString`](Classes/HiddenString.md) + * `Symmetric\Crypto::decryptWithAD`(`string`, [`EncryptionKey`](Classes/Symmetric/EncryptionKey.md), `string`): [`HiddenString`](Classes/HiddenString.md) * Asymmetric - * Anonymous - * `Asymmetric\Crypto::seal`(`HiddenString`, [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md), `bool?`): `string` - * `Asymmetric\Crypto::unseal`(`string`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), `bool?`): `HiddenString` - * Authenticated - * `Asymmetric\Crypto::encrypt`(`HiddenString`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md), `bool?`): `string` - * `Asymmetric\Crypto::decrypt`(`string`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md), `bool?`): `HiddenString` - * Authentication + * Anonymous + * `Asymmetric\Crypto::seal`([`HiddenString`](Classes/HiddenString.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md)): `string` + * `Asymmetric\Crypto::unseal`(`string`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md)): [`HiddenString`](Classes/HiddenString.md) + * Authenticated + * `Asymmetric\Crypto::encrypt`([`HiddenString`](Classes/HiddenString.md), [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md)): `string` + * `Asymmetric\Crypto::encryptWithAD`([`HiddenString`](Classes/HiddenString.md), [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md), `string`): `string` + * `Asymmetric\Crypto::decrypt`(`string`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md)): [`HiddenString`](Classes/HiddenString.md) + * `Asymmetric\Crypto::decryptWithAD`(`string`, [`EncryptionSecretKey`](Classes/Asymmetric/EncryptionSecretKey.md), [`EncryptionPublicKey`](Classes/Asymmetric/EncryptionPublicKey.md), `string`): [`HiddenString`](Classes/HiddenString.md) +* Authentication * Symmetric - * `Symmetric\Crypto::authenticate`(`string`, [`AuthenticationKey`](Classes/Symmetric/AuthenticationKey.md), `bool?`): `string` - * `Symmetric\Crypto::verify`(`string`, [`AuthenticationKey`](Classes/Symmetric/AuthenticationKey.md), `string`, `bool?`): `bool` + * `Symmetric\Crypto::authenticate`(`string`, [`AuthenticationKey`](Classes/Symmetric/AuthenticationKey.md)): `string` + * `Symmetric\Crypto::verify`(`string`, [`AuthenticationKey`](Classes/Symmetric/AuthenticationKey.md), `string`): `bool` * Asymmetric - * `Asymmetric\Crypto::sign`(`string`, [`SignatureSecretKey`](Classes/Asymmetric/SignatureSecretKey.md), `bool?`): `string` - * `Asymmetric\Crypto::verify`(`string`, [`SignaturePublicKey`](Classes/Asymmetric/SignaturePublicKey.md), `string`, `bool?`): `bool` + * `Asymmetric\Crypto::sign`(`string`, [`SignatureSecretKey`](Classes/Asymmetric/SignatureSecretKey.md)): `string` + * `Asymmetric\Crypto::verify`(`string`, [`SignaturePublicKey`](Classes/Asymmetric/SignaturePublicKey.md), `string`): `bool` Most of the other [Halite features](Features.md) build on top of these simple APIs. @@ -67,7 +71,7 @@ Later, you can load it like so: $enc_key = KeyFactory::loadEncryptionKey('/path/to/encryption.key'); ``` -Or if you want to store it in a string +Or if you want to store it in a string, rather than on the filesystem: ```php $key_hex = KeyFactory::export($enc_key)->getString(); @@ -94,7 +98,7 @@ $ciphertext = \ParagonIE\Halite\Symmetric\Crypto::encrypt( ); ``` -By default, `Crypto::encrypt()` will return a hexadecimal encoded string. If you +By default, `Crypto::encrypt()` will return a base64url-encoded string. If you want raw binary, simply pass `true` as the third argument (similar to the API used by PHP's `hash()` function). @@ -113,6 +117,57 @@ instance of `\ParagonIE\Halite\Symmetric\EncryptionKey`. If you're attempting to decrypt a raw binary string rather than a hex-encoded string, pass `true` to the third argument of `Crypto::decrypt`. +#### Additional Associated Data + +Sometimes encrypting a message isn't sufficient protection, and you also want to +bind an encrypted message to some context. Usually, this happens when you're concerned +with Confused Deputy Attacks. + +The simplest way to accomplish this is to use Halite's `EncryptWithAD()` and `DecryptWithAD()` +methods. + +**Note:** The Additional Associated Data is **NOT** stored in the encrypted message. +You must manage these strings yourself to ensure successful decryption. + +```php +use ParagonIE\HiddenString\HiddenString; + +$ad = 'Additional data that must be passed to both encrypt and decrypt calls'; + +$ciphertext = \ParagonIE\Halite\Symmetric\Crypto::encryptWithAD( + new HiddenString( + "Your message here. Any string content will do just fine." + ), + $enc_key, + $ad +); +``` + +This string must also be provided in the other direction: + +```php +$plaintext = \ParagonIE\Halite\Symmetric\Crypto::decryptWithAD( + $ciphertext, + $enc_key, + $ad +); +``` + +This will not succeed: + +```php +try { + \ParagonIE\Halite\Symmetric\Crypto::decryptWithAD( + $ciphertext, + $enc_key, + 'Incorrect String' + ); +} catch (\ParagonIE\Halite\Alerts\HaliteAlert $ex) { + var_dump($ex->getMessage()); + exit; +} +``` + ### Authenticated Asymmetric-Key Encryption (Encrypting) This API facilitates message encryption between to participants in a @@ -131,8 +186,6 @@ $send_to_bob = sodium_bin2hex($alice_public->getRawKeyMaterial()); Alice will then load Bob's public key into the appropriate object like so: ```php -use ParagonIE\HiddenString\HiddenString; - $bob_public = new \ParagonIE\Halite\Asymmetric\EncryptionPublicKey( new HiddenString( sodium_hex2bin($recv_from_bob) @@ -168,6 +221,34 @@ $message = \ParagonIE\Halite\Asymmetric\Crypto::decrypt( ); ``` +#### Additional Associated Data with Asymmetric Encryption + +If you've read the section on Symmetric Encryption, this should be unsurprising. + +```php +$ad = 'Additional Data that must be asserted on decrypt'; + +$send_to_bob = \ParagonIE\Halite\Asymmetric\Crypto::encryptWithAD( + new HiddenString( + "Your message here. Any string content will do just fine." + ), + $alice_secret, + $bob_public, + $ad +); +``` + +And decryption is similarly straightforward: + +```php +$message = \ParagonIE\Halite\Asymmetric\Crypto::decryptWithAD( + $received_ciphertext, + $alice_secret, + $bob_public, + $ad +); +``` + ### Anonymous Asymmetric-Key Encryption (Sealing) A sealing interface is one where you encrypt a message with a public key, such diff --git a/doc/Classes/Alerts/FileError.md b/doc/Classes/Alerts/FileError.md new file mode 100644 index 0000000..1068243 --- /dev/null +++ b/doc/Classes/Alerts/FileError.md @@ -0,0 +1,5 @@ +# FileError extends [HaliteAlert](HaliteAlert.md) + +**Namespace**: `\ParagonIE\Halite\Alerts` + +This indicates a filesystem error occurred. diff --git a/doc/Classes/Alerts/FileModified.md b/doc/Classes/Alerts/FileModified.md index 6f0263a..feccf3b 100644 --- a/doc/Classes/Alerts/FileModified.md +++ b/doc/Classes/Alerts/FileModified.md @@ -1,4 +1,4 @@ -# FileModified extends [HaliteAlert](HaliteAlert.md) +# FileModified extends [FileError](FileError.md) **Namespace**: `\ParagonIE\Halite\Alerts` diff --git a/doc/Classes/Alerts/HaliteAlertInterface.md b/doc/Classes/Alerts/HaliteAlertInterface.md new file mode 100644 index 0000000..f2a5cc5 --- /dev/null +++ b/doc/Classes/Alerts/HaliteAlertInterface.md @@ -0,0 +1,5 @@ +# HaliteAlertInterface extends Throwable + +**Namespace**: `\ParagonIE\Halite\Alerts` + +This is just a common interface for all Halite Alerts. diff --git a/doc/Classes/Asymmetric/Crypto.md b/doc/Classes/Asymmetric/Crypto.md index 5398e84..63a74bb 100644 --- a/doc/Classes/Asymmetric/Crypto.md +++ b/doc/Classes/Asymmetric/Crypto.md @@ -6,11 +6,14 @@ ### `getSharedSecret()` -> `public` getSharedSecret([`EncryptionSecretKey`](EncryptionSecretKey.md) `$privateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$publicKey`, `$get_as_object = false`) : [`EncryptionKey`](../Symmetric/EncryptionKey.md) +> `public` getSharedSecret([`EncryptionSecretKey`](EncryptionSecretKey.md) `$privateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$publicKey`, `$get_as_object = false`, [`?Config`](Config.md) `$config = null`) : [`EncryptionKey`](../Symmetric/EncryptionKey.md) This method calculates a shared [`EncryptionKey`](../Symmetric/EncryptionKey.md) using X25519 (Elliptic Curve Diffie Hellman key agreement over Curve25519). +In Halite v5+, this X25519 output is processed with HKDF-BLAKE2b to ensure a uniformly +random bit string is returned, rather than merely a random group element. + ### `encrypt()` > `public` encrypt(`HiddenString $source`, [`EncryptionSecretKey`](EncryptionSecretKey.md) `$ourPrivateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$theirPublicKey`, `$encoding = Halite::ENCODE_BASE64URLSAFE`) : `string` @@ -44,17 +47,23 @@ This method will: key (step 4). 7. Return what should be the original plaintext. -### `encryptWithAd()` +### `encryptWithAD()` + +> `public` encryptWithAD(`HiddenString $plaintext`, [`EncryptionSecretKey`](EncryptionSecretKey.md) `$ourPrivateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$theirPublicKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `string` + +This is similar to `encrypt()`, except the `$additionalData` string is covered by the Message Authentication Code (MAC). -> `public` encryptWithAd(`HiddenString $plaintext`, [`EncryptionSecretKey`](EncryptionSecretKey.md) `$ourPrivateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$theirPublicKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `string` +Since Halite v5, this uses the [PAE](https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Common.md#pae-definition) +concept from PASETO. -This is similar to `encrypt()`, except the `$additionalData` string is prepended to the ciphertext (after the nonce) when calculating the Message Authentication Code (MAC). +### `decryptWithAD()` -### `decryptWithAd()` +> `public` decryptWithAD(`string $ciphertext`, [`EncryptionSecretKey`](EncryptionSecretKey.md) `$ourPrivateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$theirPublicKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `HiddenString` -> `public` decryptWithAd(`string $ciphertext`, [`EncryptionSecretKey`](EncryptionSecretKey.md) `$ourPrivateKey`, [`EncryptionPublicKey`](EncryptionPublicKey.md) `$theirPublicKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `HiddenString` +This is similar to `decrypt()`, except the `$additionalData` string is covered by the Message Authentication Code (MAC). -This is similar to `decrypt()`, except the `$additionalData` string is prepended to the ciphertext (after the nonce) when calculating the Message Authentication Code (MAC). +Since Halite v5, this uses the [PAE](https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Common.md#pae-definition) +concept from PASETO. ### `seal()` diff --git a/doc/Classes/File.md b/doc/Classes/File.md index 88800e0..04b0bd9 100644 --- a/doc/Classes/File.md +++ b/doc/Classes/File.md @@ -36,6 +36,34 @@ Both `$input` and `$output` can be a string, a resource, or an object whose clas In the object case, `$input` must be an instance of [`ReadOnlyFile`](Stream/ReadOnlyFile.md) and `$output` must be an instance of [`MutableFile`](Stream/MutableFile.md). +### `asymmetricDecrypt()` + +> `public static` asymmetricDecrypt(`$input`, `$output`, [`EncryptionSecretKey`](Asymmetric/EncryptionSecretKey.md) `$recipientSK`, [`EncryptionPublicKey`](Asymmetric/EncryptionPublicKey.md) `$senderPK`, `string $aad = null`): `int` + +Decrypt the contents of `$input` (either a string containing the path to a file, or an open file +handle), and store it in the file (handle?) at `$output`. + +Both `$input` and `$output` can be a string, a resource, or an object whose class implements `StreamInterface`. +In the object case, `$input` must be an instance of [`ReadOnlyFile`](Stream/ReadOnlyFile.md) and `$output` must +be an instance of [`MutableFile`](Stream/MutableFile.md). + +The difference between `asymmetricDecrypt()` and `deseal()` is that `asymmetricDecrypt()` authenticates the sender, +while `unseal()` does not. (You can think of `unseal()` as anonymous public-key decryption.) + +### `asymmetricEncrypt()` + +> `public static` asymmetricEncrypt(`$input`, `$output`, [`EncryptionPublicKey`](Asymmetric/EncryptionPublicKey.md) `$recipientPK`, [`EncryptionSecretKey`](Asymmetric/EncryptionSecretKey.md) `$senderSK`, `string $aad = null`): `int` + +Encrypt the contents of `$input` (either a string containing the path to a file, or an open file +handle), and store it in the file (handle?) at `$output`. + +Both `$input` and `$output` can be a string, a resource, or an object whose class implements `StreamInterface`. +In the object case, `$input` must be an instance of [`ReadOnlyFile`](Stream/ReadOnlyFile.md) and `$output` must +be an instance of [`MutableFile`](Stream/MutableFile.md). + +The difference between `asymmetricEncrypt()` and `seal()` is that `asymmetricEncrypt()` authenticates the sender, while +`seal()` does not. (You can think of `seal()` as anonymous public-key encryption.) + ### `seal()` > `public static` seal(`$input`, `$output`, [`EncryptionPublicKey`](Asymmetric/EncryptionPublicKey.md) `$key`): `string` @@ -68,7 +96,7 @@ Calculate a digital signature of a file. ### `verify()` -> `public static` sign(`$input`, [`SignaturePublicKey`](Asymmetric/SignaturePublicKey.md) `$key`, `string $signature`, `boolean $raw_binary`): `bool` +> `public static` verify(`$input`, [`SignaturePublicKey`](Asymmetric/SignaturePublicKey.md) `$key`, `string $signature`, `boolean $raw_binary`): `bool` Verifies a digital signature of a file. diff --git a/doc/Classes/README.md b/doc/Classes/README.md index 653dc05..c968f7b 100644 --- a/doc/Classes/README.md +++ b/doc/Classes/README.md @@ -6,8 +6,10 @@ * [`\ParagonIE\Halite\Alerts\CannotSerializeKey`](Alerts/CannotSerializeKey.md) * [`\ParagonIE\Halite\Alerts\ConfigDirectiveNotFound`](Alerts/ConfigDirectiveNotFound.md) * [`\ParagonIE\Halite\Alerts\FileAccessDenied`](Alerts/FileAccessDenied.md) + * [`\ParagonIE\Halite\Alerts\FileError`](Alerts/FileError.md) * [`\ParagonIE\Halite\Alerts\FileModified`](Alerts/FileModified.md) * [`\ParagonIE\Halite\Alerts\HaliteAlert`](Alerts/HaliteAlert.md) (Base Exception for all Alerts) + * [`\ParagonIE\Halite\Alerts\HaliteAlertInterface`](Alerts/HaliteAlertInterface.md) (Common Interface) * [`\ParagonIE\Halite\Alerts\InvalidDigestLength`](Alerts/InvalidDigestLength.md) * [`\ParagonIE\Halite\Alerts\InvalidFlags`](Alerts/InvalidFlags.md) * [`\ParagonIE\Halite\Alerts\InvalidKey`](Alerts/InvalidKey.md) diff --git a/doc/Classes/Symmetric/Crypto.md b/doc/Classes/Symmetric/Crypto.md index 9b7d560..b9b5ec9 100644 --- a/doc/Classes/Symmetric/Crypto.md +++ b/doc/Classes/Symmetric/Crypto.md @@ -45,13 +45,19 @@ Verify-then-decrypt a message. This method will: > `public` encryptWithAd(`HiddenString $plaintext`, [`EncryptionKey`](EncryptionKey.md) `$secretKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `string` -This is similar to `encrypt()`, except the `$additionalData` string is prepended to the ciphertext (after the nonce) when calculating the Message Authentication Code (MAC). +This is similar to `encrypt()`, except the `$additionalData` string is covered by the Message Authentication Code (MAC). -### `decryptWithAd()` +Since Halite v5, this uses the [PAE](https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Common.md#pae-definition) +concept from PASETO. -> `public` decryptWithAd(`string $ciphertext`, [`EncryptionKey`](EncryptionKey.md) `$secretKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `HiddenString` +### `decryptWithAD()` -This is similar to `decrypt()`, except the `$additionalData` string is prepended to the ciphertext (after the nonce) when calculating the Message Authentication Code (MAC). +> `public` decryptWithAD(`string $ciphertext`, [`EncryptionKey`](EncryptionKey.md) `$secretKey`, `string $additionalData = ''`, `$encoding = Halite::ENCODE_BASE64URLSAFE`): `HiddenString` + +This is similar to `decrypt()`, except the `$additionalData` string is covered by the Message Authentication Code (MAC). + +Since Halite v5, this uses the [PAE](https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Common.md#pae-definition) +concept from PASETO. ### `verify()` diff --git a/doc/Classes/Util.md b/doc/Classes/Util.md index 0aa7647..aabac89 100644 --- a/doc/Classes/Util.md +++ b/doc/Classes/Util.md @@ -51,6 +51,16 @@ Returns a copy of a string without triggering PHP's optimizations. The string returned by this method can safely be used with `sodium_memzero()` without corrupting other copies of the same string. +### `splitKeys()` + +Splits a single key into two distinct keys (one for encryption, one for authentication). + +Since Halite v5, the HKDF salt parameter is not used. Instead, this randomness is appended +to the HKDF info parameter, in order to meet the [standard security definition for HKDF](https://eprint.iacr.org/2010/264). + +Additionally, this allows us to reuse the PRK (the value affected by the HKDF salt) value +for both derived keys, which results in a nice performance gain. + ### `xorStrings()` > `public static` xorStrings(`string $left`, `string $right`): `string` diff --git a/doc/Primitives.md b/doc/Primitives.md index bec1e7c..0fabd1d 100644 --- a/doc/Primitives.md +++ b/doc/Primitives.md @@ -1,16 +1,17 @@ # Cryptography Primitives used in Halite * Symmetric-key encryption: (note: only [authenticated encryption](https://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken) is available through Halite) - * [**XChaCha20**](https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xchacha20) - * Previously, [**XSalsa20**](https://paragonie.com/book/pecl-libsodium/read/08-advanced.md#crypto-stream) + * [**XChaCha20**](https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xchacha20) then BLAKE2b-MAC + * Previously, [**XSalsa20**](https://paragonie.com/book/pecl-libsodium/read/08-advanced.md#crypto-stream) then BLAKE2b-MAC * Symmetric-key authentication: **[BLAKE2b](https://download.libsodium.org/doc/hashing/generic_hashing.html#singlepart-example-with-a-key)** (keyed) -* Asymmetric-key encryption: [**X25519**](https://paragonie.com/book/pecl-libsodium/read/08-advanced.md#crypto-scalarmult) followed by symmetric-key authenticated encryption +* Asymmetric-key encryption: [**X25519**](https://paragonie.com/book/pecl-libsodium/read/08-advanced.md#crypto-scalarmult) + then [**HKDF-BLAKE2b**](Classes/Util.md#raw_keyed_hash), followed by symmetric-key authenticated encryption * Asymmetric-key digital signatures: [**Ed25519**](https://paragonie.com/book/pecl-libsodium/read/05-publickey-crypto.md#crypto-sign) * Checksums: [**BLAKE2b**](https://paragonie.com/book/pecl-libsodium/read/06-hashing.md#crypto-generichash) -* Key splitting: [**HKDF-BLAKE2b**](Classes/Util.md) +* Key splitting: [**HKDF-BLAKE2b**](Classes/Util.md#splitkeys) * Password-Based Key Derivation: [**Argon2**](https://paragonie.com/book/pecl-libsodium/read/07-password-hashing.md#crypto-pwhash-str) -In all cases, we follow an Encrypt then MAC construction, thus avoiding the [cryptographic doom principle](https://moxie.org/2011/12/13/the-cryptographic-doom-principle.html). +In all cases, we follow an Encrypt-then-MAC construction, thus avoiding the [cryptographic doom principle](https://moxie.org/2011/12/13/the-cryptographic-doom-principle.html). As a consequence of our use of a keyed BLAKE2b hash as a MAC, instead of GCM/Poly1305, Halite ciphertexts are [**message committing**](https://eprint.iacr.org/2020/1456) which makes ciphertexts random key robust. diff --git a/doc/README.md b/doc/README.md index 0d6ef7f..d97456f 100644 --- a/doc/README.md +++ b/doc/README.md @@ -13,8 +13,10 @@ * [`\ParagonIE\Halite\Alerts\CannotSerializeKey`](Classes/Alerts/CannotSerializeKey.md) * [`\ParagonIE\Halite\Alerts\ConfigDirectiveNotFound`](Classes/Alerts/ConfigDirectiveNotFound.md) * [`\ParagonIE\Halite\Alerts\FileAccessDenied`](Classes/Alerts/FileAccessDenied.md) + * [`\ParagonIE\Halite\Alerts\FileError`](Classes/Alerts/FileError.md) * [`\ParagonIE\Halite\Alerts\FileModified`](Classes/Alerts/FileModified.md) * [`\ParagonIE\Halite\Alerts\HaliteAlert`](Classes/Alerts/HaliteAlert.md) (Base Exception for all Alerts) + * [`\ParagonIE\Halite\Alerts\HaliteAlertInterface`](Classes/Alerts/HaliteAlertInterface.md) (Common Interface) * [`\ParagonIE\Halite\Alerts\InvalidDigestLength`](Classes/Alerts/InvalidDigestLength.md) * [`\ParagonIE\Halite\Alerts\InvalidFlags`](Classes/Alerts/InvalidFlags.md) * [`\ParagonIE\Halite\Alerts\InvalidKey`](Classes/Alerts/InvalidKey.md)