Recommend this page to a friend! |
![]() |
Info | Example | ![]() |
![]() |
![]() |
Reputation | Support forum | Blog | Links |
Ratings | Unique User Downloads | Download Rankings | ||||
Not enough user ratings | Total: 99 | All time: 9,822 This week: 53![]() |
Version | License | PHP version | Categories | |||
ciphersweet 1.0 | MIT/X Consortium ... | 5 | PHP 5, Databases, Cryptography, Security |
First, you'll need to decide if you have any strict operational requirements for
your encryption. This mostly boils down to whether or not you need all
encryption to be FIPS 140-2 compliant or not, in which case, you'll need to use
the FIPSCrypto
backend.
If you aren't sure, the answer is that you probably don't, and feel free to use
ModernCrypto
instead.
<?php
use ParagonIE\CipherSweet\Backend\FIPSCrypto;
use ParagonIE\CipherSweet\Backend\ModernCrypto;
$fips = new FIPSCrypto(); // Use only FIPS 140-2 algorithms
$nacl = new ModernCrypto(); // Uses libsodium
After you choose your backend, you'll need a KeyProvider. We provide a few out-of-the-box, but we also provide an interface that can be used to integrate with any key management service in your code.
The simplest example of this is the StringProvider
, which accepts a
string containing your encryption key:
<?php
use ParagonIE\ConstantTime\Hex;
use ParagonIE\CipherSweet\Backend\ModernCrypto;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;
$provider = new StringProvider(
new ModernCrypto(),
// Example key, chosen randomly, hex-encoded:
'4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc'
);
You can pass a raw binary string, hex-encoded string, or
base64url-encoded string to the second parameter of the StringProvider
constructor, provided the decoded key is 256 bits.
Attempting to pass a key of an invalid size (i.e. not 256-bit) will
result in a CryptoOperationException
being thrown. The recommended
way to generate a key is:
<?php
use ParagonIE\ConstantTime\Hex;
var_dump(Hex::encode(random_bytes(32)));
Once you have these two, you can actually start the engine (CipherSweet
).
Building on the previous code example:
<?php
use ParagonIE\ConstantTime\Hex;
use ParagonIE\CipherSweet\Backend\FIPSCrypto;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;
$provider = new StringProvider(
new ModernCrypto(),
// Example key, chosen randomly, hex-encoded:
'4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc'
);
$engine = new CipherSweet($provider);
If you're using FIPSCrypto instead of ModernCrypto, you just need to pass
it once to the KeyProvider
and the rest is handled for you.
<?php
use ParagonIE\ConstantTime\Hex;
use ParagonIE\CipherSweet\Backend\ModernCrypto;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;
$provider = new StringProvider(
new FIPSCrypto(),
// Example key, chosen randomly, hex-encoded:
'4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc'
);
$engine = new CipherSweet($provider);
Once you have an engine in play, you can start defining encrypted fields and defining one or more blind index to be used for fast search operations.
This will primarily involve the EncryptedField
class (as well as one or more
instances of BlindIndex
), mostly:
For example, the following code encrypts a user's social security number and then creates two blind indexes: One for a literal search, the other only matches the last 4 digits.
<?php
use ParagonIE\CipherSweet\BlindIndex;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\EncryptedField;
use ParagonIE\CipherSweet\Transformation\LastFourDigits;
/ @var CipherSweet $engine */
$ssn = (new EncryptedField($engine, 'contacts', 'ssn'))
// Add a blind index for the "last 4 of SSN":
->addBlindIndex(
new BlindIndex(
// Name (used in key splitting):
'contact_ssn_last_four',
// List of Transforms:
[new LastFourDigits()],
// Bloom filter size (bits)
16
)
)
// Add a blind index for the full SSN:
->addBlindIndex(
new BlindIndex(
'contact_ssn',
[],
32
)
);
// Some example parameters:
$contactInfo = [
'name' => 'John Smith',
'ssn' => '123-45-6789',
'email' => 'foo@example.com'
];
/
* @var string $ciphertext
* @var array<string, string> $indexes
*/
list ($ciphertext, $indexes) = $ssn->prepareForStorage($contactInfo['ssn']);
Every time you run the above code, the $ciphertext
will be randomized, but the
array of blind indexes will remain the same.
Each blind index returns an array with two values: type
and value
. The value
is calculated from the plaintext. The type
is a key derived form the table
name, field name, and index name.
The type
indicator is handy if you're storing all your blind indexes in a
separate table rather than in an additional column in the same table. In the
latter case, you only need the value
string for each index.
var_dump($ciphertext, $indexes);
/*
string(73) "nacl:jIRj08YiifK86YlMBfulWXbatpowNYf4_vgjultNT1Tnx2XH9ecs1TqD59MPs67Dp3ui"
array(2) {
["contact_ssn_last_four"]=>
array(2) {
["type"]=>
string(13) "3dywyifwujcu2"
["value"]=>
string(4) "8058"
}
["contact_ssn"]=>
array(2) {
["type"]=>
string(13) "2iztg3wbd7j5a"
["value"]=>
string(8) "311314c1"
}
}
*/
You can now use these values for inserting/updating records into your database.
To search the database at a later date, use getAllBlindIndexes()
or getBlindIndex()
:
<?php
use ParagonIE\CipherSweet\BlindIndex;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\EncryptedField;
use ParagonIE\CipherSweet\Transformation\LastFourDigits;
/ @var CipherSweet $engine */
$ssn = (new EncryptedField($engine, 'contacts', 'ssn'))
// Add a blind index for the "last 4 of SSN":
->addBlindIndex(
new BlindIndex(
// Name (used in key splitting):
'contact_ssn_last_four',
// List of Transforms:
[new LastFourDigits()],
// Bloom filter size (bits)
16
)
)
// Add a blind index for the full SSN:
->addBlindIndex(
new BlindIndex(
'contact_ssn',
[],
32
)
);
// Use these values in search queries:
$indexes = $ssn->getAllBlindIndexes('123-45-6789');
$lastFour = $ssn->getBlindIndex('123-45-6789', 'contact_ssn_last_four');
Which should result in the following (for the example key):
var_dump($lastFour);
/*
array(2) {
["type"]=>
string(13) "3dywyifwujcu2"
["value"]=>
string(4) "8058"
}
*/
var_dump($indexes);
/*
array(2) {
["contact_ssn_last_four"]=>
array(2) {
["type"]=>
string(13) "3dywyifwujcu2"
["value"]=>
string(4) "8058"
}
["contact_ssn"]=>
array(2) {
["type"]=>
string(13) "2iztg3wbd7j5a"
["value"]=>
string(8) "311314c1"
}
}
*/
An alternative approach for datasets with multiple encrypted rows and/or
encrypted boolean fields
is the EncryptedRow
API, which looks like this:
<?php
use ParagonIE\CipherSweet\BlindIndex;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\CompoundIndex;
use ParagonIE\CipherSweet\EncryptedRow;
use ParagonIE\CipherSweet\Transformation\LastFourDigits;
/ @var CipherSweet $engine */
// Define two fields (one text, one boolean) that will be encrypted
$row = (new EncryptedRow($engine, 'contacts'))
->addTextField('ssn')
->addBooleanField('hivstatus');
// Add a normal Blind Index on one field:
$row->addBlindIndex(
'ssn',
new BlindIndex(
'contact_ssn_last_four',
[new LastFourDigits()],
32 // 32 bits = 4 bytes
)
);
// Create/add a compound blind index on multiple fields:
$row->addCompoundIndex(
(
new CompoundIndex(
'contact_ssnlast4_hivstatus',
['ssn', 'hivstatus'],
32, // 32 bits = 4 bytes
true // fast hash
)
)->addTransform('ssn', new LastFourDigits())
);
// Notice: You're passing an entire array at once, not a string
$prepared = $row->prepareRowForStorage([
'extraneous' => true,
'ssn' => '123-45-6789',
'hivstatus' => false
]);
var_dump($prepared);
/*
array(2) {
[0]=>
array(3) {
["extraneous"]=>
bool(true)
["ssn"]=>
string(73) "nacl:wVMElYqnHrGB4hU118MTuANZXWHZjbsd0uK2N0Exz72mrV8sLrI_oU94vgsWlWJc84-u"
["hivstatus"]=>
string(61) "nacl:ctWDJBn-NgeWc2mqEWfakvxkG7qCmIKfPpnA7jXHdbZ2CPgnZF0Yzwg="
}
[1]=>
array(2) {
["contact_ssn_last_four"]=>
array(2) {
["type"]=>
string(13) "3dywyifwujcu2"
["value"]=>
string(8) "805815e4"
}
["contact_ssnlast4_hivstatus"]=>
array(2) {
["type"]=>
string(13) "nqtcc56kcf4qg"
["value"]=>
string(8) "cbfd03c0"
}
}
}
*/
With the EncryptedRow
API, you can encrypt a subset of all of the fields
in a row, and create compound blind indexes based on multiple pieces of
data in the dataset rather than a single field, without writing a ton of
glue code.
CipherSweet is database-agnostic, so you'll need to write some code that uses CipherSweet behind-the-scenes to encrypt data before storing it in a database, query the database based on blind indexes, and then use CipherSweet to decrypt the results.
See also: the examples directory.
See also: the solutions directory.
CipherSweet is a backend library developed by Paragon Initiative Enterprises for implementing searchable field-level encryption.
Requires PHP 5.5+, although 7.2 is recommended for better performance.
Before adding searchable encryption support to your project, make sure you understand the appropriate threat model for your use case. At a minimum, you will want your application and database server to be running on separate cloud instances / virtual machines. (Even better: Separate bare-metal hardware.)
CipherSweet is available under the very permissive ISC License which allows you to use CipherSweet in any of your PHP projects, commercial or noncommercial, open source or proprietary, at no cost to you.
Use Composer.
composer require paragonie/ciphersweet
Please refer to the documentation to learn how to use CipherSweet.
Please feel free to create an issue if you'd like to integrate CipherSweet with your software.
CipherSweet was originally intend for use in in SuiteCRM (a fork of the SugarCRM Community Edition) and related products, although there is nothing preventing its use in other products.
Therefore, we opted for a pun on "ciphersuite" that pays homage to the open source heritage of the project we designed this library for.
If the wordplay is too heavy, feel free to juxtapose the two component nouns and call it "SweetCipher" in spoken conversation.
![]() |
File | Role | Description | ||
---|---|---|---|---|
![]() |
||||
![]() |
||||
![]() |
||||
![]() ![]() |
Data | Auxiliary data | ||
![]() ![]() |
Data | Auxiliary data | ||
![]() ![]() |
Lic. | License text | ||
![]() ![]() |
Data | Auxiliary data | ||
![]() ![]() |
Data | Auxiliary data | ||
![]() ![]() |
Doc. | Documentation |
The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page. |
![]() |
Version Control | Unique User Downloads | Download Rankings | |||||||||||||||
100% |
|
|
Applications that use this package |
If you know an application of this package, send a message to the author to add a link here.