Category Archives: PHP

Authenticated Encrypt with OpenSSL and PHP 7.1

One of the missing feature of the OpenSSL extension for PHP was the support for Authenticated Encryption. If you are wondering why the authentication is important for encryption, I suggest to have a look at this presentation.
The issue with OpenSSL was related to the API design of the functions openssl_encrypt and openssl_decrypt that didn’t return the authentication hash of the encrypted data.

string openssl_encrypt(
    string $data,
    string $method,
    string $password,
    [ int $options = 0 ],
    [ string $iv = "" ]
)

string openssl_decrypt(
    string $data,
    string $method,
    string $password,
    [ int $options = 0 ],
    [ string $iv = "" ]
)

The output of openssl_encrypt is the encrypted data, without the authentication hash. This hash is required to decrypt a message, because the algorithm needs to authenticate the data before proceed with decryption.

This issue will be solved from PHP 7.1 thanks to RFC openssl_aead proposed by Jakub Zelenka. The idea is to add some optional parameters to the previous OpenSSL functions. The new API is reported below (the new parameters are marked in gray):

string openssl_encrypt(
    string $data,
    string $method,
    string $password,
    [ int $options = 0 ],
    [ string $iv = "" ],
    [ string &$tag = NULL ],
    [ string $aad = "" ],
    [ int $tag_length = 16 ]
)

string openssl_decrypt(
    string $data,
    string $method,
    string $password,
    [ int $options = 0 ],
    [ string $iv = "" ],
    [ string $tag = "" ],
    [ string $aad = "" ]
)

The authentication hash is stored in the $tag variable. This value is filled by the openssl_encrypt function and returned as reference.

The other optional parameter $aad represents additional authentication data that you could use to protect the message against alterations, without the encryption part. For instance, if you need to encrypt an email leaving the header information in plaintext, like the sender and the receiver, you can pass the header in $aad.

The last optional parameter $tag_length is the length in bytes of the hash value, that is 16 by default. This value is related to the encryption algorithm used. I will give some details on it later.

To decrypt an authenticated message, you need to pass the $tag value to openssl_decrypt and optionally the additional authenticated data ($aad).

Until PHP 7.1 will not be available, you can test this new feature using a Release Candidate version of PHP 7.1, for instance 7.1.0RC05.

The OpenSSL extension provides the support for two authenticated encryption algorithm: GCM and CCM. I will show how to use it in the next sections.

Galois/Counter Mode (GCM)

The Galois/Counter Mode (GCM) is a mode of operation for symmetric key cryptographic block ciphers that provides encryption and authentication. If you are interested in the details of this algorithm you can read the Wikipedia page.
This is an algorithm used in many applications like IPsec, SSH and TLS 1.2. Used together with AES (AES-GCM) is included in the NSA Suite B Cryptography .
This algorithm is very fast because the execution can be parallelized. Moreover, the algorithm does not any patents and can be used without restrictions.

You can check if your OpenSSL extension supports the GCM mode using the openssl_get_cipher_methods function. If you see “-gcm” or “-GCM” at the end of a cipher name you can use the Galois/Counter Mode. You need to have at least OpenSSL 1.1 to support this algorithm.

Below is reported an example using aes-256-gcm algorithm (i.e. AES block cipher with 256 bit key):

$algo = 'aes-256-gcm';
$iv   = random_bytes(openssl_cipher_iv_length($algo));
$key  = random_bytes(32); // 256 bit
$data = random_bytes(1024); // 1 Kb of random data
$ciphertext = openssl_encrypt(
    $data, 
    $algo, 
    $key, 
    OPENSSL_RAW_DATA, 
    $iv, 
    $tag
);
// Change 1 bit in ciphertext
// $i = rand(0, mb_strlen($ciphertext, '8bit') - 1);
// $ciphertext[$i] = $ciphertext[$i] ^ chr(1);
$decrypt = openssl_decrypt(
    $ciphertext, 
    $algo, 
    $key, 
    OPENSSL_RAW_DATA, 
    $iv,
    $tag
);
if (false === $decrypt) {
    throw new Exception(sprintf(
        "OpenSSL error: %s", openssl_error_string()
    ));
}
printf ("Decryption %s\n", $data === $decrypt ? 'Ok' : 'Failed');

If you need to store the encrypted value somewhere you need to store the $tag value concatenated with $ciphertext. This because you need to pass the $tag again to decrypt the ciphertext. If you store the $tag value you need to remember also the size of this value. I suggest to use the default value of 16 bytes to simplify the usage. The tag length for the GCM mode can be between 4 and 16 bytes.

During the decryption part, we can check for errors if the $decrypt value is false. For instance, if someone has altered the encrypted message we can recognize it because the authentication will fail. You can uncomment the lines 13 and 14 from the previous example to simulate a change in the $ciphertext. Unfortunately, in the case of authentication error the openssl_error_string function returns an empty string. I just opened this report at bugs.php.net.

If you want to use additional authenticated data you can pass the string to be authenticated in the $aad parameter, as reported in the following example:

$algo  = 'aes-256-gcm';
$iv    = random_bytes(openssl_cipher_iv_length($algo));
$key   = random_bytes(32); // 256 bit
$email = 'This is the secret message!';
$aad   = 'From: foo@domain.com, To: bar@domain.com'; 
$ciphertext = openssl_encrypt(
    $email, 
    $algo, 
    $key, 
    OPENSSL_RAW_DATA, 
    $iv, 
    $tag,
    $aad
);
// Change 1 bit in additional authenticated data
// $i = rand(0, mb_strlen($aad, '8bit') - 1);
// $aad[$i] = $aad[$i] ^ chr(1);
$decrypt = openssl_decrypt(
    $ciphertext, 
    $algo, 
    $key, 
    OPENSSL_RAW_DATA, 
    $iv,
    $tag,
    $aad
);
if (false === $decrypt) {
    throw new Exception(sprintf(
        "OpenSSL error: %s", openssl_error_string()
    ));
}
printf ("Decryption %s\n", $email === $decrypt ? 'Ok' : 'Failed');

In this case, if you uncomment the lines 15 and 16 you will have an authentication error regarding the $aad part. Notice that the encrypted message in this case is composed by $tag . $aad . $ciphertext. Basically, you need to store also the $aad in plaintext to be able to perform the authentication during the decryption of the message.

Counter with CBC-MAC (CCM)

Counter with CBC-MAC (CCM) is another authenticated encryption mode for symmetric block ciphers. The CCM mode is also used in many applications like IPsec and TLS 1.2 and is part of the IEEE 802.11i standard. CCM is an alternative implementations of the OCB mode, that was originally covered by patents. The CCM can be used without any restriction.
I will not show the details of the CCM algorithm, if you are interested you can read the Wikipedia page.
This encryption mode can be used in PHP 7.1 if your OpenSSL extension support the “-ccm” or “-CCM” in the name of the algorithm.

The previous example code works also in the case of CCM, you need only to replace the first line with :

$algo  = 'aes-256-ccm';

The only difference with GCM, related to the OpenSSL usage, is the size of $tag. CCM has no limits of tag’s length and also the resulted tag is different for each length.

Benchmark GCM vs. CCM

I provided a very simple benchmark script to test the performance of encryption and decryption using GCM and CCM mode. The result is that GCM is 3x faster than CCM. This results is as expected due to the differences between the two algorithms. That said, my suggestion is to use GCM in your code!

This is the script that I used for the benchmark:

$key = random_bytes(32);
$data = random_bytes(1024 * 1024 * 10); // 10 Mb
$iv = random_bytes(openssl_cipher_iv_length('aes-256-gcm'));

$start = microtime(true);
$ciphertext = openssl_encrypt($data, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$gcmTimeEnc = microtime(true) - $start;

$start = microtime(true);
$decrypt = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$gcmTimeDec = microtime(true) - $start;
if ($decrypt !== $data) {
  throw new Exception("Decryption failed for GCM");
}

$iv = random_bytes(openssl_cipher_iv_length('aes-256-ccm'));
$start = microtime(true);
$ciphertext = openssl_encrypt($data, 'aes-256-ccm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$ccmTimeEnc = microtime(true) - $start;

$start = microtime(true);
$decrypt = openssl_decrypt($ciphertext, 'aes-256-ccm', $key, OPENSSL_RAW_DATA, $iv, $tag);
$ccmTimeDec = microtime(true) - $start;
if ($decrypt !== $data) {
  throw new Exception("Decryption failed for CCM");
}

printf("GCM (enc): %.4f, GCM (dec): %.4f\n", $gcmTimeEnc, $gcmTimeDec);
printf("CCM (enc): %.4f, CCM (dec): %.4f\n", $ccmTimeEnc, $ccmTimeDec);

I executed this script using PHP7.1RC5, CPU Intel Core i7, 8 GB RAM, 256 GB SSD, Ubuntu 16.04.

Note: All the PHP scripts used in this post are available here.

Install PHP 7 on Ubuntu 14.04

Update: if you are using Ubuntu 15.04, you can have a look at this script by Maulik Mistry.

I just installed PHP 7.0.0-dev (based on PHPNG) on my GNU/Linux box (Ubuntu 14.04) and I found some errors during the procedure. I decided to write this note to help people using the same Ubuntu environment.

Before to start I would like to remind that PHP 7 is still in development and SHOULD NOT BE USED in production environments. I installed to try and experiment the new features of the language. The first stable release of PHP 7 is scheduled by the end of the year, with a projected release date of November 2015.

To install PHP 7 we need to clone the php-src repository, configure and compile. Let’s create a php7 folder in the home directory and clone the project:

mkdir $HOME/php7
cd $HOME/php7
git clone https://git.php.net/repository/php-src.git

After that, we need to prepare and configure the compiler. We need to execute the following commands:

cd php-src
./buildconf
./configure \
    --prefix=$HOME/php7/usr \
    --with-config-file-path=$HOME/php7/usr/etc \
    --enable-mbstring \
    --enable-zip \
    --enable-bcmath \
    --enable-pcntl \
    --enable-ftp \
    --enable-exif \
    --enable-calendar \
    --enable-sysvmsg \
    --enable-sysvsem \
    --enable-sysvshm \
    --enable-wddx \
    --with-curl \
    --with-mcrypt \
    --with-iconv \
    --with-gmp \
    --with-pspell \
    --with-gd \
    --with-jpeg-dir=/usr \
    --with-png-dir=/usr \
    --with-zlib-dir=/usr \
    --with-xpm-dir=/usr \
    --with-freetype-dir=/usr \
    --with-t1lib=/usr \
    --enable-gd-native-ttf \
    --enable-gd-jis-conv \
    --with-openssl \
    --with-mysql=/usr \
    --with-pdo-mysql=/usr \
    --with-gettext=/usr \
    --with-zlib=/usr \
    --with-bz2=/usr \
    --with-recode=/usr \
    --with-mysqli=/usr/bin/mysql_config

During the execution of configure command, I found a couple of errors.

Error: Your t1lib distribution is not installed correctly.

I fixed using:

sudo apt-get install libt1-dev

Error: Unable to locate gmp.h

I tried to install the libgmp-dev library:

sudo apt-get install libgmp-dev

but the library was already installed, so I search for it:

locate gm.h

and I found it on /usr/include/x86_64-linux-gnu/gmp.h (I’m using a 64bit version).

I tried to symlink it to /usr/include/gmp.h (the default location for include):

ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h 

and finally the configure execution was successful. If you find other errors on your environment I suggest to read this post by Maciej Zgadzaj, where he reported a list of possible errors and solutions for Ubuntu systems.

Now it’s time to compile PHP 7 with the following commands:

make
make install

The first command compile the PHP. I took about 9 minutes to compile PHP 7 on my computer (Intel i5-2500 at 3.3Ghz). The second command install the PHP modules and configuration in $HOME/php7/usr folder.

We have almost done the installation, we just need to create the php.ini file in the $HOME/php7/usr/etc folder. We can easily create it using vi with the following commands:

mkdir $HOME/php7/usr/etc
vi $HOME/php7/usr/etc/php.ini

We can use the following content for the php.ini:

max_execution_time=600
memory_limit=128M
error_reporting=0
display_errors=0
log_errors=0
user_ini.filename=
realpath_cache_size=2M
cgi.check_shebang_line=0

zend_extension=opcache.so
opcache.enable_cli=1
opcache.save_comments=0
opcache.fast_shutdown=1
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.use_cwd=1
opcache.max_accelerated_files=100000
opcache.max_wasted_percentage=5
opcache.memory_consumption=128
opcache.consistency_checks=0

And finally we can test PHP using the command line interface (CLI):

$HOME/php7/php-src/sapi/cli/php -v

You will get a result like this:

PHP 7.0.0-dev (cli) (built: xxx) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies

If you want to learn about the new features of PHP 7 I suggest to read the following resources:

Documenting APIs using Apigility

One of the cool feature of Apigility is the ability to generate API documentation using a simple UI. The documentation is generated in HTML format, and optionally in Swagger format. The API documentation is reported in Apigility in the top bar, under the menu “API Docs” (Figure 1, using Apigility 1.0.0beta1).

API documentationFigure 1

In order to generate the API documentation you need to insert some desciptions before. All the information to edit are reported in the Documentation tab on each REST or RPC service (Figure 2).

REST API documentationFigure 2

For each service and for each HTTP method, you can specify a description of the action. In case of RESTful services you can also specify different information for an Entity and a Collection. An interesting feature of the API documentation is the ability to generate the Response Body specification from the configuration, using the “generate from configuration” button (Figure 3).

Generate from documentationFigure 3

This button read the configuration of the API and propose a JSON response based on the fields specified (the fields are documented under the Fields tab of each REST and RPC service). Of course, you can edit the response body changing the output, if you need.

Once you have added some API descriptions, you can go to the “API Docs” menu and show the API documentation (in our case version 1, Figure 4).

API documentation in HTML formatFigure 4

You will see all the API documentation in HTML format, using the Bootstrap 3 template.
You can expand and collapse the information on each HTTP method clicking on the name. All the API documentation are exposed in the /apigility/documentation base URL.

How to install the Swagger adapter

To activate the Swagger adapter for the API documentation, you need to add the following dependency in the composer.json file (in the require field):

"zfcampus/zf-apigility-documentation-swagger": "~1.0-dev"

and execute the composer update commmand.

After the installation of zf-apigility-documentation-swagger you need to enable this module in the config/application.config.php file. You have to edit this configuration file and add the following line after the ‘ZF\Apigility\Documentation’:

'ZF\Apigility\Documentation\Swagger',

Now you can go to the Swagger documentation from the welcome screen, clicking on the Swagger API documentation button, or going directly to the /apigility/swagger URL.
To show the Swagger UI render you have to select the API service version and you will see a web page like the one reported in Figure 5 (using Swagger UI).

API documentation in Swagger formatFigure 5

Customizing the API documentation module

The API documentation feature is offered by Apigility using the zf-apigility-documentation module, written in Zend Framework 2. This module provide an object model of all captured documentation information, including:

  • All APIs available
  • All Services available in each API
  • All Operations available in each API
  • All required/expected Accept and Content-Type request headers, and expected Content-Type response header, for each available API Service Operation.
  • All configured fields for each service

Moreover, it provides a configurable MVC endpoint for returning documentation

  • documentation will be delivered in a serialized JSON structure by default
  • end-users may configure alternate/additional formats via content-negotiation

If you want to customize the format of your API documentation you can have a look at the source code of the zf-apigility-documentation-swagger module. Basically, you need to create a custom route for your format (see the Swagger module.config.php) and use the ZF\Apigility\Documentation\ApiFactory to access the data for the API documentation services. The view model to implement needs to manage a list view and a show view, that’s it.

All the API documentation formats are driven by content negotiation (using the zf-content-negotiation module). For instance, to get the API documentation data in Swagger format you can use the content negotiation “application/vnd.swagger+json”.

For example, if you want to retrieve the API documentation data in JSON format you can use the following request (using HTTPie):

http GET http://localhost:8888/apigility/documentation[/api]/[service] 'Accept:application/json'

where [api] is the name of the API and [service] is the name of the REST or RPC service. To get the same result in Swagger format you can use the following request:

http GET http://localhost:8888/apigility/documentation/[api]/[service] 'Accept:application/vnd.swagger+json'