The phpthemis extension provides PHP access to the features and functions of the Themis cryptographic library:
There are also example console utils available for the PHP wrapper for Themis (as well as for some other wrappers — see the full list here). They help understand the specific mechanics of encryption/decryption processes of this specific wrapper. You can find the example console utils for the PHP wrapper here.
If you prefer building from sources — dive here.
PHP versions 5.6, 7.0, 7.1 or 7.2 installed in your system.
1. Import the public key used by Cossack Labs to sign packages:
wget -qO - https://pkgs.cossacklabs.com/gpg | sudo apt-key add -
Note: If you wish to validate key fingerprint, it is:
29CF C579 AD90 8838 3E37 A8FA CE53 BCCA C8FF FACB
.
2. You may need to install the apt-transport-https package before proceeding:
sudo apt-get install apt-transport-https
3. Add Cossack Labs repository to your sources.list
:
sudo add-apt-repository \ "deb https://pkgs.cossacklabs.com/stable/$(lsb_release -is | awk '{print tolower($0)}') \ $(lsb_release -cs) \ main"
4. Update the apt package index:
sudo apt-get update
5. Choose and install Themis extension for your PHP version:
apt-cache search libphpthemis
sudo apt-get install libphpthemis-php<version>
Check for successful setup:
php --ri phpthemis
In addition to the common set of build tools, Themis currently requires either the OpenSSL or LibreSSL package with the developer version of the package (as it provides header files). In either case, we strongly recommend you using the most recent version of these packages.
Get Themis source code from GitHub
git clone https://github.com/cossacklabs/themis.git
Note that the default installation process assumes the use of the standard LibreSSL/OpenSSL library and will install Themis to the standard /usr/lib
and /usr/include
locations.
In a typical case, all you need to do is (depending on your rights, we assume unprivileged user shell with sudo available):
cd themis
sudo make install
If your path is different, please see Building and installing for more details on how to change it.
To build and run tests, you can use:
make test
All the tests need to be passed with no errors.
Once the Themis library is installed, do:
sudo make phpthemis_install
To run tests for php test suit, you can run:
make prepare_tests_all test_php
This will build and install phpthemis.so
to the standard zend extension directory. To enable the extension, you should add this to your php.ini
file:
extension=phpthemis.so
You can then verify if phpthemis is installed using:
php --ri phpthemis
Some code samples for Themis objects are available in docs/examples/php folder.
We've also prepared tests in tests/phpthemis. Run them using following line:
./tests/phpthemis/run_tests.sh
Themis supports both Elliptic Curve and RSA algorithms for asymmetric cryptography. Algorithm type is chosen according to the generated key type. Asymmetric keys are needed for Secure Message and Secure Session objects.
WARNING: When you distribute private keys to your users, make sure the keys are sufficiently protected. You can find the guidelines here.
NOTE: When using public keys of other peers, make sure they come from trusted sources.
Elliptic Curve
To generate an Elliptic Curve key pair:
mixed phpthemis_gen_ec_key_pair( void )
Return Values
Returns an associative array containing a public and private 256 bit EC keys on success. NULL is returned on error. The keys contained in the array values are strings containing binary data.
$keys = phpthemis_gen_ec_key_pair(); if ($keys !== NULL) { $private_key = $keys['private_key']; $public_key = $keys['public_key']; }
RSA
To generate an RSA key pair:
mixed phpthemis_gen_rsa_key_pair( void )
Return Values
Returns an associative array containing a public and private key on success. NULL is returned on error. The keys contained in the array values are strings containing binary data.
$keys = phpthemis_gen_rsa_key_pair(); if ($keys !== NULL) { $private_key = $keys['private_key']; $public_key = $keys['public_key']; }
The Secure Message functions provide a sequence-independent, stateless, contextless messaging system. This may be preferred in cases that don't require frequent sequential message exchange and/or in low-bandwidth contexts. This is secure enough to exchange messages from time to time, but if you'd like to have Perfect Forward Secrecy and higher security guarantees, please consider using Secure Session instead.
The Secure Message functions offer two modes of operation:
In Sign/Verify mode, the message is signed using the sender's private key and is verified by the receiver using the sender's public key. The message is packed in a suitable container and ECDSA is used by default to sign the message (when RSA key is used, RSA+PSS+PKCS#7 digital signature is used).
In Encrypt/Decrypt mode, the message will be encrypted with a randomly generated key (in RSA) or a key derived by ECDH (in ECDSA), via symmetric algorithm with Secure Cell in seal mode (keys are 256 bits long).
The mode is selected by the sender supplying a valid public key of the receiver (encrypt/decrypt) or setting this parameter to NULL, or an empty string to use sign/verify.
Read more about Secure Message's cryptographic internals here.
mixed phpthemis_secure_message_wrap( string $senders_private_key, string $receivers_public_key, string $message ) mixed phpthemis_secure_message_unwrap( string $receivers_private_key, string $senders_public_key, string $secure_message )
Parameters:
phpthemis_secure_message_wrap
is used for encryption/signing; returns encrypted or signed message, returns a string of binary data containing the encrypted or signed message on success. NULL is returned on error.senders_private_key
is the private (EC or RSA) key of the entity sending the message — it is assumed that the receiver has safely acquired the associated public key through other channels.receivers_public_key
is the public (EC or RSA) key of the entity receiving the message — it is assumed that the sender has safely acquired this key through other channels. message
plaintext message.
phpthemis_secure_message_unwrap
is used for decryption/verifying; returns a string containing the original message on success. NULL is returned on error.
receivers_private_key
is the private (EC or RSA) key of the entity receiving the message. senders_public_key
is the public (EC or RSA) key of the entity sending the message - it is assumed that the receiver has safely acquired this key through other means. secure_message
encrypted/signed message. Encrypt/Decrypt:
$sender_keys = phpthemis_gen_ec_key_pair(); $receiver_keys = phpthemis_gen_ec_key_pair(); $message = 'The best laid schemes of mice and men go oft awry'; $smessage = phpthemis_secure_message_wrap($sender_keys['private_key'],$receiver_keys['public_key'],$message); $rmessage = phpthemis_secure_message_unwrap($receiver_keys['private_key'], $sender_keys['public_key'],$message_to_send); echo "Received : $rmessage\n";
Sign/Verify:
$sender_keys = phpthemis_gen_ec_key_pair(); $receiver_keys = phpthemis_gen_ec_key_pair(); $message = 'The best laid schemes of mice and men go oft awry'; // Passing NULL as receiver keys enables Sign/Verify mode $smessage = phpthemis_secure_message_wrap($sender_keys['private_key'],NULL,$message); $rmessage = phpthemis_secure_message_unwrap(NULL, $sender_keys['public_key'], $message_to_send); echo "Received : $rmessage\n";
The Secure Сell functions provide the means of protection for arbitrary data contained in stores, such as database records or filesystem files. These functions provide both strong symmetric encryption and data authentication mechanisms.
The general approach is that given:
Secure Cell functions will produce:
The purpose of the optional "context information" (i.e. a database row number or file name) is to establish a secure association between this context and the protected data. In short, even when the password is known, if the context is incorrect, then decryption will fail.
The purpose of the authentication data is to validate that given a correct password (and context), the decrypted data is indeed the same as the original source data.
The authentication data must be stored somewhere. The most convenient way is to simply be appended it to the encrypted data, but this is not always possible due to the storage architecture of your application. The Secure Cell functions offer variants that address this issue in different ways.
The encryption algorithm used by Secure Cell (by default) is AES-256. The generated authentication data is 16 bytes long.
Secure Cell is available in 3 modes:
You can learn more about the underlying considerations, limitations, and features here.
Encryption:
mixed phpthemis_scell_seal_encrypt( string $password, string $source_data [, string $context] )
Parameters:
password
the password to be used for encryption.source_data
the data to be encrypted.context
the optional context (see above).Return Values
Returns a string of binary data containing the encrypted data with the authentication data appended on success. NULL is returned on error.
Decryption:
mixed phpthemis_scell_seal_decrypt( string $password, string $encrypted_data [, string $context] )
Parameters:
password
the password to be used for decryption.encrypted_data
the data to be decrypted.context
the optional context (see above).Return Values
On success returns a string of binary data containing the original data. NULL is returned on error.
$password = 'not-so-secret'; $message = 'The best laid schemes of mice and men go oft awry'; $context = '12345'; $secure_cell = phpthemis_scell_seal_encrypt($password, $message, $context); $decrypted = phpthemis_scell_seal_decrypt($password, $secure_cell, $context); echo "Decrypted: $decrypted\n";
For cases where it is not feasible to simply append authentication data to the encrypted data, the phpthemis_scell_token_protect_encrypt
and phpthemis_scell_token_protect_decrypt
functions allow these two elements to be handled separately.
Encryption:
mixed phpthemis_scell_token_protect_encrypt( string $password, string $source_data [, string $context] )
Parameters:
password
the password to be used for encryption.source_data
the data to be encrypted.context
the optional context (see above).Return Values
Returns an associative array, the elements of which are strings of binary data containing the encrypted data and the authentication data on success. NULL is returned on error.
Decryption:
mixed phpthemis_scell_token_protect_decrypt(string $password, string $encrypted_data, string $token [, string $context] )
Parameters:
password
password to be used for decryption.encrypted_data
data to be decrypted.token
relevant authentication data (token).context
optional context (see above).Return Values
Returns a string of binary data containing the original data on success. NULL is returned on error.
$password = 'not-so-secret'; $message = 'The best laid schemes of mice and men go oft awry'; $context = '12345'; $secure_cell = phpthemis_scell_token_protect_encrypt($password, $message, $context); $decrypted = phpthemis_scell_token_protect_decrypt($password, $secure_cell['encrypted_message'], $secure_cell['token'], $context); echo "Decrypted: $decrypted\n";
When it's impossible to simply append authentication data to the encrypted data, and when there are no auxiliary storage media to retain authentication data, the phpthemis_scell_context_imprint_encrypt
and phpthemis_scell_context_imprint_decrypt
functions provide encryption with the user supplied context, but without the benefit of authentication. This means that the integrity of the data cannot be enforced and these functions should only be the preferred choice when the alternatives above are not viable.
Note: In Context Imprint mode the context is mandatory.
Encryption:
mixed phpthemis_scell_context_imprint_encrypt( string $password, string $source_data, string $context )
Parameters:
password
the password to be used for encryption.source_data
the data to be encrypted.context
the mandatory context (see above).Return Values
Returns a string of binary data containing the encrypted data without any authentication data on success. NULL is returned on error.
Decryption:
mixed phpthemis_scell_context_imprint_decrypt( string $password, string $encrypted_data , string $context )
Parameters:
password
the password to be used for decryption.encrypted_data
the data to be decrypted.context
the mandatory context (see above).Return Values
Returns a string of binary data containing the original data on success. NULL is returned on error.
$password = 'not-so-secret'; $message = 'The best laid schemes of mice and men go oft awry'; $context = '12345'; $secure_cell = phpthemis_scell_context_imprint_encrypt($password, $message, $context); $decrypted = phpthemis_scell_context_imprint_decrypt($password, $secure_cell, $context); echo "Decrypted: $decrypted\n";
Secure Session is a sequence- and session- dependent, stateful messaging system. It is suitable for protecting long-lived peer-to-peer message exchanges where the secure data exchange is bound to a specific session context.
Secure Session operates in two stages: session negotiation where the keys are established and cryptographic material is exchanged to generate ephemeral keys and data exchange the where exchanging of messages can be carried out between peers.
You can read a more detailed description of the process here.
As noted above, Secure Session is stateful. It is therefore implemented by phpthemis as an object rather than as static functions. It's important to note at this point that persisting a Secure Session object (for example across HTTP requests to PHP as either a CGI or an Apache Module) would:
a) present a range of unwanted security issues and
b) is simply not supported.
Thus PHP use of Secure Session should only be considered in the context of daemonised PHP processes and there are definitely alternatives to that you may wish to consider.
Note: The phpthemis implementation uses exceptions to handle error states.
Put simply, Secure Session takes the following form:
In order to focus on the specific functionality of Secure session (rather than the communication required between the client and the server), the example code below simulates a client / server interaction.
function get_pub_key_by_id($id) { global $key_store; $pubkey = ''; if(!empty($key_store[$id])) { $pubkey = $key_store[$id]['public_key']; } return($pubkey); }
The function name get_pub_key_by_id
is required. The function should return a public key generated by phpthemis_gen_ec_key_pair
or phpthemis_gen_rsa_key_pair
.
The client and server keys are generated and stored in a global (accessible to the get_pub_key_by_id
) function above. Both the client and the server session objects are constructed.
A Secure Session is established and messages are exchanged.
Note: The second and the third message do not require session negotiation.
global $key_store; // An arbitrary global //Set up client and server keys $key_store['server'] = phpthemis_gen_ec_key_pair(); $key_store['client'] = phpthemis_gen_ec_key_pair(); // Create Secure Session Objects try { $server_session = new themis_secure_session('server', $key_store['server']['private_key']); $client_session = new themis_secure_session('client', $key_store['client']['private_key']); } catch (Exception $e) { echo "Session setup failed ...".$e->getMessage()."\n"; exit; } // Test messages: $response=secure_session_client('This is a test message 1',$client_session,$server_session); echo "Receiving ... (".$response['status'].") ".$response['message']."\n"; $response=secure_session_client('This is a test message 2',$client_session,$server_session); echo "Receiving ... (".$response['status'].") ".$response['message']."\n"; $response=secure_session_client('This is a test message 3',$client_session,$server_session); echo "Receiving ... (".$response['status'].") ".$response['message']."\n";
Initially, if the session is not established, the client generates a connect request. Subsequently, the client "negotiates" with the server by replying with its stateful interpretation of the server's response. Once "negotiation" is complete, the initial application level message is sent and the server's response is processed.
function secure_session_client($message_to_send,$client_session,$server_session) { $client_message = ''; $server_response = array('status' => 0, 'message' => ''); try { if (!$client_session->is_established()) { echo "Connecting ... \n"; $client_message = $client_session->connect_request(); } $ii = 1; while (!$client_session->is_established()) { echo "Negotiating ... ".$ii++."\n"; $server_response=secure_session_server($client_message,$server_session); if ($server_response['status'] != 0) {return($server_response);} $client_message=$client_session->unwrap($server_response['message']); } //With the session established handle the actual message to send echo "Sending ... ".$message_to_send." \n"; $client_message = $client_session->wrap($message_to_send); $server_response = secure_session_server($client_message,$server_session); if ($server_response['status'] != 0) { return($server_response); } $server_response['message'] = $client_session->unwrap($server_response['message']); } catch (Exception $e) { $server_response['status'] =-2; // A Client Error $server_response['message']=$e->getMessage(); } return($server_response); }
A minimal "application" level protocol returns an associative array containing "status" (0 for success, -1 on error) and "message" containing either the negotiation phase data, the application level response, or the exception message on error.
function secure_session_server($client_message,$server_session) { $server_response = array('status' => 0, 'message' => ''); try { if (!$server_session->is_established()) { $server_response['message'] = $server_session->unwrap($client_message); } else { $client_message = $server_session->unwrap($client_message); $server_response['message'] = $server_session->wrap('Response to: '.$client_message); } } catch (Exception $e) { $server_response['status'] =-1; // A Server Error $server_response['message']=$e->getMessage(); } return($server_response); }
That's it! See the full example available in docs/examples/php.