Last year, we began developing [Halite](https://github.com/paragonie/halite), a [FOSS](https://en.wikipedia.org/wiki/Free_and_open-source_software) high-level wrapper for [the PHP bindings to libsodium](https://pecl.php.net/package/libsodium). We use Halite extensively in our own projects (including our [upcoming CMS](https://github.com/paragonie/airship) which has quite a few of its own [innovative cryptography features](https://paragonie.com/blog/2016/05/keyggdrasil-continuum-cryptography-powering-cms-airship) baked-in). As of version `2.1.0`, we are confident that Halite solves all of the application-layer cryptography problems that most PHP developers face; and it does so in three easy steps. (For transport-layer cryptography, you should still use TLS, of course.) ## The Three Steps to Simply Secure Cryptography Development
<?php
use \ParagonIE\Halite\KeyFactory;
// Generate a new random encryption key:
$encryptionKey = KeyFactory::generateEncryptionKey();
Once you have a cryptography key, you can save it to a file and then reload it like so:
// Saving a key to a file:
KeyFactory::save($encryptionKey, '/path/to/encryption.key');
// Loading the key from a file:
$key = KeyFactory::loadEncryptionKey('/path/to/encryption.key');
That's all there is to managing keys.
1. Generate a new key, which will be secure, then save it somewhere.
2. Load it every time you need to use it.
Alternatively, if you wish to derive the key from a password, you can just do this:
<?php
use \ParagonIE\Halite\KeyFactory;
// Do this ONCE per key:
$salt = random_bytes(16);
// Generate a new random encryption key:
$encryptionKey = KeyFactory::deriveEncryptionKey(
$password,
$salt // Generated by random_bytes(16), never rand() or mt_rand()
KeyFactory::MODERATE // (This third parameter is totally optional.)
// also allows KeyFactory::INTERACTIVE or KeyFactory::SENSITIVE
// defaults to KeyFactory::INTERACTIVE
);
There are six types of keys Halite uses, which correspond to distinct [cryptography features](https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded):
* Symmetric Cryptography (both parties know the same key)
* `AuthenticationKey` - used for authenticating messages
* `EncryptionKey` - used for encrypting messages
* Asymmetric Cryptography (both parties have their own secret key, and exchange their public keys)
* `EncryptionPublicKey`
* `EncryptionSecretKey`
* `SignaturePublicKey`
* `SignatureSecretKey`
To make keys easier to organize, you can just generate a `KeyPair` object for asymmetric keys. This isn't necessary; if you have the `*SecretKey` you can produce the corresponding `*PublicKey`:
$signSecretKey = KeyFactory::generateSignatureSecretKey();
$signPublicKey = $signSecretKey->derivePublicKey();
// $signPublicKey is a SignaturePublicKey object
<?php
use \ParagonIE\Halite\{
Symmetric\Crypto as Symmetric,
KeyFactory
};
// Loading the key from a file:
$key = KeyFactory::loadEncryptionKey('/path/to/encryption.key');
// And now, to encrypt a message:
$encrypted = Symmetric::encrypt('this is your plaintext', $key);
You don't need to worry about accidentally passing a weak key to `encrypt()`; it requires an `EncryptionKey` object. You don't need to know what a nonce is; Halite generates it for you. You don't need to worry about [chosen-ciphertext attacks](https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly); Halite strictly uses authenticated encryption behind-the-scenes.
See below for how to decrypt these messages.
<?php
use \ParagonIE\Halite\{
Symmetric\Crypto as Symmetric,
KeyFactory
};
// Loading the key from a file:
$key = KeyFactory::loadAuthenticationKey('/path/to/authentication.key');
// And now, to authenticate a message:
$authCode = Symmetric::auth('this is your plaintext', $key);
See below for how to verify these messages.
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
$aliceSecretKey = KeyFactory::loadEncryptionSecretKey('/path/to/enc.secret.key');
$BobPublicKey = KeyFactory::loadEncryptionPublicKey('/path/to/bob.enc.public.key');
// And now, to encrypt a message:
$encrypted = Asymmetric::encrypt(
'this is your plaintext',
$aliceSecretKey,
$bobPublicKey
);
The anonymous case uses `seal()` and `unseal()`, like so:
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
$bobPublicKey = KeyFactory::loadEncryptionPublicKey('/path/to/bob.enc.public.key');
// And now, to encrypt a message:
$encrypted = Asymmetric::seal(
'this is your plaintext',
$bobPublicKey
);
See below for how to decrypt these messages.
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
// Loading the key from a file:
$secretKey = KeyFactory::loadSignatureSecretKey('/path/to/sign.secret.key');
// And now, to authenticate a message:
$signature = Asymmetric::sign('this is your message', $secretKey);
See below for how to verify digital signatures.
$plaintext = Symmetric::decrypt($encrypted, $key);
If the contents of `$encrypted` were tampered with, instead of getting a result, it will throw an `\ParagonIE\Halite\Alerts\InvalidMessage` exception.
You may choose to catch the exception (i.e. to handle the failure gracefully for the sake of consistent user experience). If you do not catch it, it will terminate script execution rather than fail open. (If you somehow forget to turn `display_errors` off, your keys will not be exposed by a stack trace.)
This will never return `FALSE`; there is no need to check the return value.
if (Symmetric::verify('this is your plaintext', $key, $authCode)) {
// Success
} else {
// Invalid authentication code for this message
}
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
$bobSecretKey = KeyFactory::loadEncryptionSecretKey('/path/to/enc.secret.key');
$alicePublicKey = KeyFactory::loadEncryptionPublicKey('/path/to/alice.enc.public.key');
// And now, to decrypt a message:
$plaintext = Asymmetric::decrypt(
$encrypted,
$bobSecretKey,
$alicePublicKey
);
The `unseal()` API is as simple as `seal()`:
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
$bobSecretKey = KeyFactory::loadEncryptionSecretKey('/path/to/enc.secret.key');
// And now, to encrypt a message:
$plaintext = Asymmetric::unseal(
$encrypted,
$bobSecretKey
);
As with symmetric-key decryption, if the encrypted message is tampered with in-transit, these methods will throw an `InvalidMessage` exception rather than return `FALSE`.
<?php
use \ParagonIE\Halite\{
Asymmetric\Crypto as Asymmetric,
KeyFactory
};
// Loading the key from a file:
$publicKey = KeyFactory::loadSignaturePublicKey('/path/to/sign.public.key');
if (Asymmetric::verify($message, $publicKey, $signature)) {
// Valid signature for this message
} else {
// Incorrect signature
}
### There is No Step Four
That's it. You've just solved 90% of the cryptography use-cases in three easy steps:
1. Generate/derive keys (and store them for later use).
2. Encrypt/authenticate your messages.
3. Decrypt/verify your messages.
It could hardly be any simpler while still being secure.
> What about the other 10% of use-cases?
Halite has most of them covered as well, and they also only require up to two steps to implement once your keys exist. Read on to learn more about any specific solution to a problem you encounter.
<?php
use \ParagonIE\Halite\{
KeyFactory,
Password
};
$key = KeyFactory::loadEncryptionKey('/path/to/encryption.key');
$hash = Password::hash($password, $key);
As with key derivation, you may pass an optional `KeyFactory::MODERATE` or `KeyFactory::SENSITIVE` to the third parameter to increase the Argon2i cost factors. This probably isn't necessary (and may expose your servers to DoS attacks without proper rate-limiting).
Verifying passwords (and checking if a re-hash is needed):
if (Password::verify($password, $hash, $key)) {
// Login successful, but first, check that our hash is still good (i.e. in case Halite updates):
if (Password::needsRehash($hash, $key, KeyFactory::INTERACTIVE)) {
$replaceStoredHash = Password::hash($password, $key);
}
} else {
// Incorrect username or password
}
<?php
use \ParagonIE\Halite\{
File,
KeyFactory
};
$bobPublicKey = KeyFactory::loadEncryptionPublicKey('/path/to/bob.enc.public.key');
// And now, to encrypt a message:
File::seal(
'/path/to/input.file',
'/path/to/output.file',
$bobPublicKey
);
The recipient only needs to run:
File::unseal(
'/path/to/encrypted.file',
'/path/to/decrypted.file',
$bobSecretKey
);
This feature is fully [documented here](https://github.com/paragonie/halite/blob/master/doc/Classes/File.md).
<?php
use \ParagonIE\Halite\Structure\{
MerkleTree,
Node
};
$tree = new MerkleTree(
new Node('GENESIS BLOCK'),
new Node('Next piece of data'),
new Node('Yet another piece of data')
);
var_dump($tree->getRoot());
// string(64) "15343b62af2602fc9d0c93b440a4b0f6c745537a094e890c6bef08bebf71a72b"
A Merkle Tree is a building block for more powerful cryptography features (e.g. [Keyggdrasil](https://paragonie.com/blog/2016/05/keyggdrasil-continuum-cryptography-powering-cms-airship#keyggdrasil)). It effectively provides an append-only data structure that can be independently audited (sans any practical hash collisions).
As of Halite 2.1.0, you can adjust the hash size of your Merkle tree as well as personalize all of the BLAKE2b hashes with a string unique to your application.
$tree->setPersonalizationString('some constant');
var_dump($tree->getRoot());
// string(64) "087d6d429ae665915bce2652c6036acc9d4584834179884887005aa84074744d"
$tree->setHashSize(16); // Raw bytes, not hexits
var_dump($tree->getRoot());
// string(32) "6a77dec5b6229be067aa7cf678ce32cf"
Merkle Trees are immutable. If you wish to append another node to the tree, use the `getExpandedTree()` method, which creates a new `MerkleTree` object.
$otherTree = $tree->getExpandedTree(
new Node('Extra Data'),
new Node('Something else goes here')
);
$otherTree->setHashSize(16); // Raw bytes, not hexits
var_dump($otherTree->getRoot());
// string(32) "ed3fbf1c01399fba7fe0c43086b95113"
Expanded trees will inherit the parent tree's personalization string and hash size parameters.
$newTree = new MerkleTree(
new Node('GENESIS BLOCK'),
new Node('Next piece of data'),
new Node('Yet another piece of data'),
new Node('Extra Data'),
new Node('Something else goes here')
);
var_dump($newTree->getRoot());
// string(64) "9228af238afa1b94c30cc763a176dc3397f72fd6fca773146af40a03fc3f01f2"
<?php
use \ParagonIE\Halite\{
KeyFactory,
Cookie
};
$key = KeyFactory::loadEncryptionKey('/path/to/encryption.key');
$cookieStorage = new Cookie($key);
// Store a value in an encrypted cookie:
$cookieStorage->store('name', 'value');
// Retrieve a value from an encrypted cookie:
$value = $cookieStorage->fetch('name');
## That's Too Easy! What's the Catch?
Halite offers a lot of utility for PHP developers above and beyond what many PHP cryptography libraries try to offer. We're able to get away with covering this much ground, without introducing really dumb security vulnerabilities, because our underlying cryptography primitives are provided by libsodium.
We designed Halite to be as simple as possible so PHP developers who aren't also cryptography experts can successfully use it to its full potential. Even its internal code is, reportedly, [easy for people who don't program in PHP to read](https://twitter.com/ELagergren/status/729060314449256448).
It's time to [ditch mcrypt](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong), abolish hacky home-grown unauthenticated encryption protocols, and embrace the simple and the secure. Did I mention Halite was [Free Software](https://www.gnu.org/philosophy/free-sw.en.html)?