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
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
|
##################
Encryption Library
##################
.. important:: DO NOT use this or any other *encryption* library for
user password storage! Passwords must be *hashed* instead, and you
should do that via PHP's own `Password Hashing extension
<http://php.net/password>`_.
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 dependencies 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 dependencies 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 **must** be as long as the encyption algorithm in use
allows. For AES-128, that's 128 bits or 16 bytes (charcters) long.
You will find a table below that shows the supported key lengths of
different ciphers.
The key should be as random as possible and it **must not** be a regular
text string, nor the output of a hashing function, etc. In order to create
a proper key, you must use the Encryption library's ``create_key()`` method
::
// $key will be assigned a 16-byte (128-bit) random key
$key = $this->encryption->create_key(16);
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';
You'll notice that the ``create_key()`` method outputs binary data, which
is hard to deal with (i.e. a copy-paste may damage it), so you may use
``bin2hex()``, ``hex2bin()`` or Base64-encoding to work with the key in
a more friendly manner. For example::
// Get a hex-encoded representation of the key:
$key = bin2hex($this->encryption->create_key(16));
// Put the same value in your config with hex2bin(),
// so that it is still passed as binary to the library:
$config['encryption_key'] = hex2bin(<your hex-encoded 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, XTS
AES-192 OpenSSL 192 / 24 CBC, CTR, CFB, CFB8, OFB, ECB, XTS
AES-256 OpenSSL 256 / 32 CBC, CTR, CFB, CFB8, OFB, ECB, 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).
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 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.
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.
.. _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/manual/en/function.hash-hmac.php>`_ function.
Stronger algorithms of course will be added in the future as they
appear and become widely available.
***************
Class Reference
***************
.. php:class:: CI_Encryption
.. php: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.
.. php: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.
.. php: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.
.. php:method:: create_key($length)
:param int $length: Output length
:returns: A pseudo-random cryptographic key with the specified length, or FALSE on failure
:rtype: string
Creates a cryptographic key by fetching random data from
the operating system's sources (i.e. /dev/urandom).
.. php: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
:rtype: string
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
|