summaryrefslogtreecommitdiffstats
path: root/user_guide_src/source/libraries/encryption.rst
blob: ff41ade78d3ce6c603e55232379a7a786fe489fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
##################
Encryption Library
##################

The Encryption Library provides two-way data encryption. To do so in
a cryptographically secure way, it utilizes PHP extensions that are
unfortunately not always available on all systems.
You must meet one of the following dependancies in order to use this
library:

- `OpenSSL <http://php.net/openssl>`_ (and PHP 5.3.3)
- `MCrypt <http://php.net/mcrypt>`_ (and `MCRYPT_DEV_URANDOM` availability)

If neither of the above dependancies is met, we simply cannot offer
you a good enough implementation to meet the high standards required
for proper cryptography.

.. contents::
  :local:

.. raw:: html

  <div class="custom-index container"></div>

****************************
Using the Encryption Library
****************************

Initializing the Class
======================

Like most other classes in CodeIgniter, the Encryption library is
initialized in your controller using the ``$this->load->library()``
method::

	$this->load->library('encryption');

Once loaded, the Encryption library object will be available using::

	$this->encryption

Default behavior
================

By default, the Encryption Library will use the AES-128 cipher in CBC
mode, using your configured *encryption_key* and SHA512 HMAC authentication.

.. note:: AES-128 is chosen both because it is proven to be strong and
	because of its wide availability across different cryptographic
	software and programming languages' APIs.

However, the *encryption_key* is not used as is.

If you are somewhat familiar with cryptography, you should already know
that a HMAC also requires a secret key and using the same key for both
encryption and authentication is a bad practice.

Because of that, two separate keys are derived from your already configured
*encryption_key*: one for encryption and one for authentication. This is
done via a technique called `HMAC-based Key Derivation Function
<http://en.wikipedia.org/wiki/HKDF>`_ (HKDF).

Setting your encryption_key
===========================

An *encryption key* is a piece of information that controls the
cryptographic process and permits a plain-text string to be encrypted,
and afterwards - decrypted. It is the secret "ingredient" in the whole
process that allows you to be the only one who is able to decrypt data
that you've decided to hide from the eyes of the public.
After one key is used to encrypt data, that same key provides the **only**
means to decrypt it, so not only must you chose one carefully, but you
must not lose it or you will also use the encrypted data.

It must be noted that to ensure maximum security, such key *should* not
only be as strong as possible, but also often changed. Such behavior
however is rarely practical or possible to implement, and that is why
CodeIgniter gives you the ability to configure a single key that is to be
used (almost) every time.

It goes without saying that you should guard your key carefully. Should
someone gain access to your key, the data will be easily decrypted. If
your server is not totally under your control it's impossible to ensure
key security so you may want to think carefully before using it for
anything that requires high security, like storing credit card numbers.

Your encryption key should be as long as the encyption algorithm in use
allows. For AES-128, that's 128 bits or 16 bytes (charcters) long. The
key should be as random as possible and it should **not** be a simple
text string.

You will find a table below that shows the supported key lengths of
different ciphers.

The key can be either stored in your *application/config/config.php*, or
you can design your own storage mechanism and pass the key dynamically
when encrypting/decrypting.

To save your key to your *application/config/config.php*, open the file
and set::

	$config['encryption_key'] = 'YOUR KEY';

.. _ciphers-and-modes:

Supported encryption ciphers and modes
======================================

.. note:: The terms 'cipher' and 'encryption algorithm' are interchangeable.

Portable ciphers
----------------

Because MCrypt and OpenSSL (also called drivers throughout this document)
each support different sets of encryption algorithms and often implement
them in different ways, our Encryption library is designed to use them in
a portable fashion, or in other words - it enables you to use them
interchangeably, at least for the ciphers supported by both drivers.

It is also implemented in a way that aims to match the standard
implementations in other programming languages and libraries.

Here's a list of the so called "portable" ciphers, where
"CodeIgniter name" is the string value that you'd have to pass to the
Encryption library to use that cipher:

======================== ================== ============================ ===============================
Cipher name              CodeIgniter name   Key lengths (bits / bytes)   Supported modes
======================== ================== ============================ ===============================
AES-128 / Rijndael-128   aes-128            128 / 16                     CBC, CTR, CFB, CFB8, OFB, ECB
AES-192                  aes-192            192 / 24                     CBC, CTR, CFB, CFB8, OFB, ECB
AES-256                  aes-256            256 / 32                     CBC, CTR, CFB, CFB8, OFB, ECB
DES                      des                56 / 7                       CBC, CFB, CFB8, OFB, ECB
TripleDES                tripledes          56 / 7, 112 / 14, 168 / 21   CBC, CFB, CFB8, OFB
Blowfish                 blowfish           128-448 / 16-56              CBC, CFB, OFB, ECB
CAST5 / CAST-128         cast5              88-128 / 11-16               CBC, CFB, OFB, ECB
RC4 / ARCFour            rc4                40-2048 / 5-256              Stream
======================== ================== ============================ ===============================

.. important:: Because of how MCrypt works, if you fail to provide a key
	with the appropriate length, you might end up using a different
	algorithm than the one configured, so be really careful with that!

.. note:: In case it isn't clear from the above table, Blowfish, CAST5
	and RC4 support variable length keys. That is, any number in the
	shown ranges is valid, although in bit terms that only happens
	in 8-bit increments.

.. note:: Even though CAST5 supports key lengths lower than 128 bits
	(16 bytes), in fact they will just be zero-padded to the
	maximum length, as specified in `RFC 2144
	<http://tools.ietf.org/rfc/rfc2144.txt>`_.

.. note:: Blowfish supports key lengths as small as 32 bits (4 bytes), but
	our tests have shown that only lengths of 128 bits (16 bytes) or
	higher are properly supported by both MCrypt and OpenSSL. It is
	also a bad practice to use such low-length keys anyway.

Driver-specific ciphers
-----------------------

As noted above, MCrypt and OpenSSL support different sets of encryption
ciphers. For portability reasons and because we haven't tested them
properly, we do not advise you to use the ones that are driver-specific,
but regardless, here's a list of most of them:


============== ========= ============================== =========================================
Cipher name    Driver    Key lengths (bits / bytes)     Supported modes
============== ========= ============================== =========================================
AES-128        OpenSSL   128 / 16                       CBC, CTR, CFB, CFB8, OFB, ECB, GCM, XTS
AES-192        OpenSSL   192 / 24                       CBC, CTR, CFB, CFB8, OFB, ECB, GCM, XTS
AES-256        OpenSSL   256 / 32                       CBC, CTR, CFB, CFB8, OFB, ECB, GCM, XTS
Rijndael-128   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Rijndael-192   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Rijndael-256   MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
GOST           MCrypt    256 / 32                       CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Twofish        MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
CAST-128       MCrypt    40-128 / 5-16                  CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
CAST-256       MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Loki97         MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
SaferPlus      MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Serpent        MCrypt    128 / 16, 192 / 24, 256 / 32   CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
XTEA           MCrypt    128 / 16                       CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
RC2            MCrypt    8-1024 / 1-128                 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
RC2            OpenSSL   8-1024 / 1-128                 CBC, CFB, OFB, ECB
Camellia-128   OpenSSL   128 / 16                       CBC, CFB, CFB8, OFB, ECB
Camellia-192   OpenSSL   192 / 24                       CBC, CFB, CFB8, OFB, ECB
Camellia-256   OpenSSL   256 / 32                       CBC, CFB, CFB8, OFB, ECB
Seed           OpenSSL   128 / 16                       CBC, CFB, OFB, ECB
============== ========= ============================== =========================================

.. note:: If you wish to use one of those ciphers, you'd have to pass
	its name in lower-case to the Encryption library.

.. note:: You've probably noticed that all AES cipers (and Rijndael-128)
	are also listed in the portable ciphers list. This is because
	drivers support different modes for these ciphers. Also, it is
	important to note that AES-128 and Rijndael-128 are actually
	the same cipher, but **only** when used with a 128-bit key.

.. note:: CAST-128 / CAST-5 is also listed in both the portable and
	driver-specific ciphers list. This is because OpenSSL's
	implementation doesn't appear to be working correctly with
	key sizes of 80 bits and lower.

.. note:: RC2 is listed as supported by both MCrypt and OpenSSL.
	However, both drivers implement them differently and they
	are not portable. It is probably worth noting that we only
	found one obscure source confirming that it is MCrypt that
	is not properly implementing it.

.. _encryption-modes:

Encryption modes
----------------

Different modes of encryption have different characteristics and serve
for different purposes. Some are stronger than others, some are faster
and some offer extra features.
We are not going in depth into that here, we'll leave that to the
cryptography experts. The table below is to provide brief informational
reference to our more experienced users. If you are a beginner, just
stick to the CBC mode - it is widely accepted as strong and secure for
general purposes.

=========== ================== ================= ===================================================================================================================================================
Mode name   CodeIgniter name   Driver support    Additional info
=========== ================== ================= ===================================================================================================================================================
CBC         cbc                MCrypt, OpenSSL   A safe default choice
CTR         ctr                MCrypt, OpenSSL   Considered as theoretically better than CBC, but not as widely available
CFB         cfb                MCrypt, OpenSSL   N/A
CFB8        cfb8               MCrypt, OpenSSL   Same as CFB, but operates in 8-bit mode (not recommended).
OFB         ofb                MCrypt, OpenSSL   N/A
OFB8        ofb8               MCrypt            Same as OFB, but operates in 8-bit mode (not recommended).
ECB         ecb                MCrypt, OpenSSL   Ignores IV (not recommended).
GCM         gcm                OpenSSL           Provides authentication and therefore doesn't need a HMAC.
XTS         xts                OpenSSL           Usually used for encrypting random access data such as RAM or hard-disk storage.
Stream      stream             MCrypt, OpenSSL   This is not actually a mode, it just says that a stream cipher is being used. Required because of the general cipher+mode initialization process.
=========== ================== ================= ===================================================================================================================================================

Message Length
==============

It's probably important for you to know that an encrypted string is usually
longer than the original, plain-text string (depending on the cipher).

This is influenced by the cipher algorithm itself, the IV prepended to the
cipher-text and (unless you are using GCM mode) the HMAC authentication
message that is also prepended. Furthermore, the encrypted message is also
Base64-encoded so that it is safe for storage and transmission, regardless
of a possible character set in use.

Keep this information in mind when selecting your data storage mechanism.
Cookies, for example, can only hold 4K of information.

.. _configuration:

Configuring the library
=======================

For usability, performance, but also historical reasons tied to our old
:doc:`Encrypt Class <encrypt>`, the Encryption library is designed to
use repeatedly the same driver, encryption cipher, mode and key.

As noted in the "Default behavior" section above, this means using an
auto-detected driver (OpenSSL has a higher priority), the AES-128 ciper
in CBC mode, and your ``$config['encryption_key']`` value.

If you wish to change that however, you need to use the ``initialize()``
method. It accepts an associative array of parameters, all of which are
optional:

======== ===============================================
Option   Possible values
======== ===============================================
driver   'mcrypt', 'openssl'
cipher   Cipher name (see :ref:`ciphers-and-modes`)
mode     Encryption mode (see :ref:`encryption-modes`)
key      Encryption key 
======== ===============================================

For example, if you were to change the encryption algorithm and
mode to AES-256 in CTR mode, this is what you should do::

	$this->encryption->initialize(
		array(
			'cipher' => 'aes-256',
			'mode' => 'ctr',
			'key' => '<a 32-character random string>'
		)
	);

Note that we only mentioned that you want to change the ciper and mode,
but we also included a key in the example. As previously noted, it is
important that you choose a key with a proper size for the used algorithm.

There's also the ability to change the driver, if for some reason you
have both, but want to use MCrypt instead of OpenSSL::

	// Switch to the MCrypt driver
	$this->encryption->initialize(array('driver' => 'mcrypt'));

	// Switch back to the OpenSSL driver
	$this->encryption->initialize(array('driver' => 'openssl'));

Encrypting and decrypting data
==============================

Encrypting and decrypting data with the already configured library
settings is simple. As simple as just passing the string to the
``encrypt()`` and/or ``decrypt()`` methods::

	$plain_text = 'This is a plain-text message!';
	$ciphertext = $this->encryption->encrypt($plain_text);

	// Outputs: This is a plain-text message!
	echo $this->encryption->decrypt($ciphertext);

And that's it! The Encryption library will do everything necessary
for the whole process to be cryptographically secure out-of-the-box.
You don't need to worry about it.

.. important:: Both methods will return FALSE in case of an error.
	While for ``encrypt()`` this can only mean incorrect
	configuration, you should always check the return value
	of ``decrypt()`` in production code.

How it works
------------

If you must know how the process works, here's what happens under
the hood:

- ``$this->encryption->encrypt($plain_text)``

  #. Derive an encryption key and a HMAC key from your configured
     *encryption_key* via HKDF, using the SHA-512 digest algorithm.

  #. Generate a random initialization vector (IV).

  #. Encrypt the data via AES-128 in CBC mode (or another previously
     configured cipher and mode), using the above-mentioned derived
     encryption key and IV.

  #. Prepend said IV to the resulting cipher-text.

  #. Base64-encode the resulting string, so that it can be safely
     stored or transferred without worrying about character sets.

  #. Create a SHA-512 HMAC authentication message using the derived
     HMAC key to ensure data integrity and prepend it to the Base64
     string.

- ``$this->encryption->decrypt($ciphertext)``

  #. Derive an encryption key and a HMAC key from your configured
     *encryption_key* via HKDF, using the SHA-512 digest algorithm.
     Because your configured *encryption_key* is the same, this
     will produce the same result as in the ``encrypt()`` method
     above - otherwise you won't be able to decrypt it.

  #. Check if the string is long enough, separate the HMAC out of
     it and validate if it is correct (this is done in a way that
     prevents timing attacks agains it). Return FALSE if either of
     the checks fails.

  #. Base64-decode the string.

  #. Separate the IV out of the cipher-text and decrypt the said
     cipher-text using that IV and the derived encryption key.

.. _custom-parameters:

Using custom parameters
-----------------------

Let's say you have to interact with another system that is out
of your control and uses another method to encrypt data. A
method that will most certainly not match the above-described
sequence and probably not use all of the steps either.

The Encryption library allows you to change how its encryption
and decryption processes work, so that you can easily tailor a
custom solution for such situations.

.. note:: It is possible to use the library in this way, without
	setting an *encryption_key* in your configuration file.

All you have to do is to pass an associative array with a few
parameters to either the ``encrypt()`` or ``decrypt()`` method.
Here's an example::

	// Assume that we have $ciphertext, $key and $hmac_key
	// from on outside source

	$message = $this->encryption->decrypt(
		$ciphertext,
		array(
			'cipher' => 'blowfish',
			'mode' => 'cbc',
			'key' => $key,
			'hmac_digest' => 'sha256',
			'hmac_key' => $hmac_key
		)
	);

In the above example, we are decrypting a message that was encrypted
using the Blowfish cipher in CBC mode and authenticated via a SHA-256
HMAC.

.. important:: Note that both 'key' and 'hmac_key' are used in this
	example. When using custom parameters, encryption and HMAC keys
	are not derived like the default behavior of the library is.

Below is a list of the available options.

However, unless you really need to and you know what you are doing,
we advise you to not change the encryption process as this could
impact security, so please do so with caution.

============= =============== ============================= ======================================================
Option        Default value   Mandatory / Optional          Description
============= =============== ============================= ======================================================
cipher        N/A             Yes                           Encryption algorithm (see :ref:`ciphers-and-modes`).
mode          N/A             Yes                           Encryption mode (see :ref:`encryption-modes`).
key           N/A             Yes                           Encryption key.
                                                            If not provided it will be automatically generated
                                                            during encryption and looked for during decryption.
hmac          TRUE            No                            Whether to use a HMAC.
                                                            Boolean. If set to FALSE, then *hmac_digest* and
                                                            *hmac_key* will be ignored.
hmac_digest   sha512          No                            HMAC message digest algorithm (see :ref:`digests`).
hmac_key      N/A             Yes, unless *hmac* is FALSE   HMAC key.
raw_data      FALSE           No                            Whether the cipher-text should be raw.
                                                            Boolean. If set to TRUE, then Base64 encoding and
                                                            decoding will not be performed and HMAC will not
                                                            be a hexadecimal string.
============= =============== ============================= ======================================================

.. important:: ``encrypt()`` and ``decrypt()`` will return FALSE if
	a mandatory parameter is not provided or if a provided
	value is incorrect. This includes *hmac_key*, unless *hmac*
	is set to FALSE.

.. note:: If GCM mode is used, *hmac* will always be FALSE. This is
	because GCM mode itself provides authentication.

.. _digests:

Supported HMAC authentication algorithms
----------------------------------------

For HMAC message authentication, the Encryption library supports
usage of the SHA-2 family of algorithms:

=========== ==================== ============================
Algorithm   Raw length (bytes)   Hex-encoded length (bytes)
=========== ==================== ============================
sha512      64                   128
sha384      48                   96
sha256      32                   64
sha224      28                   56
=========== ==================== ============================

The reason for not including other popular algorithms, such as
MD5 or SHA1 is that they are no longer considered secure enough
and as such, we don't want to encourage their usage.
If you absolutely need to use them, it is easy to do so via PHP's
native `hash_hmac() <http://php.net/hash_hmac()>`_ function.

Stronger algorithms of course will be added in the future as they
appear and become widely available.

***************
Class Reference
***************

.. class:: CI_Encryption

	.. method:: initialize($params)

		:param	array	$params: Configuration parameters
		:returns:	CI_Encryption instance (method chaining)
		:rtype:	CI_Encryption

		Initializes (configures) the library to use a different
		driver, cipher, mode or key.

		Example::

			$this->encryption->initialize(
				array('mode' => 'ctr')
			);

		Please refer to the :ref:`configuration` section for detailed info.

	.. method:: encrypt($data[, $params = NULL])

		:param	string	$data: Data to encrypt
		:param	array	$params: Optional parameters
		:returns:	Encrypted data or FALSE on failure
		:rtype:	string

		Encrypts the input data and returns its ciphertext.

		Example::

			$ciphertext = $this->encryption->encrypt('My secret message');

		Please refer to the :ref:`custom-parameters` section for information
		on the optional parameters.

	.. method:: decrypt($data[, $params = NULL])

		:param	string	$data: Data to decrypt
		:param	array	$params: Optional parameters
		:returns:	Decrypted data or FALSE on failure
		:rtype:	string

		Decrypts the input data and returns it in plain-text.

		Example::

			echo $this->encryption->decrypt($ciphertext);

		Please refer to the :ref:`custom-parameters` secrion for information
		on the optional parameters.

	.. method:: hkdf($key[, $digest = 'sha512'[, $salt = NULL[, $length = NULL[, $info = '']]]])

		:param	string	$key: Input key material
		:param	string	$digest: A SHA-2 family digest algorithm
		:param	string	$salt: Optional salt
		:param	int	$length: Optional output length
		:param	string	$info: Optional context/application-specific info
		:returns:	A pseudo-random key or FALSE on failure

		Derives a key from another, presumably weaker key.

		This method is used internally to derive an encryption and HMAC key
		from your configured *encryption_key*.

		It is publicly available due to its otherwise general purpose. It is
		described in `RFC 5869 <https://tools.ietf.org/rfc/rfc5869.txt>`_.

		However, as opposed to the description in RFC 5869, this implementation
		doesn't support SHA1.

		Example::

			$hmac_key = $this->encryption->hkdf(
				$key,
				'sha512',
				NULL,
				NULL,
				'authentication'
			);

			// $hmac_key is a pseudo-random key with a length of 64 bytes