Skip to content

Commit

Permalink
Merge pull request #177 from paragonie/v5-docs
Browse files Browse the repository at this point in the history
Halite v5 Documentation
  • Loading branch information
paragonie-security authored Jan 19, 2022
2 parents 1bbf8ac + 0580191 commit 906280e
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 36 deletions.
117 changes: 99 additions & 18 deletions doc/Basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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();
Expand All @@ -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).

Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions doc/Classes/Alerts/FileError.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# FileError extends [HaliteAlert](HaliteAlert.md)

**Namespace**: `\ParagonIE\Halite\Alerts`

This indicates a filesystem error occurred.
2 changes: 1 addition & 1 deletion doc/Classes/Alerts/FileModified.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# FileModified extends [HaliteAlert](HaliteAlert.md)
# FileModified extends [FileError](FileError.md)

**Namespace**: `\ParagonIE\Halite\Alerts`

Expand Down
5 changes: 5 additions & 0 deletions doc/Classes/Alerts/HaliteAlertInterface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# HaliteAlertInterface extends Throwable

**Namespace**: `\ParagonIE\Halite\Alerts`

This is just a common interface for all Halite Alerts.
23 changes: 16 additions & 7 deletions doc/Classes/Asymmetric/Crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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()`

Expand Down
30 changes: 29 additions & 1 deletion doc/Classes/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions doc/Classes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 10 additions & 4 deletions doc/Classes/Symmetric/Crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`

Expand Down
10 changes: 10 additions & 0 deletions doc/Classes/Util.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
11 changes: 6 additions & 5 deletions doc/Primitives.md
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit 906280e

Please sign in to comment.