summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--system/libraries/Encryption.php4
-rw-r--r--tests/codeigniter/libraries/Encryption_test.php10
-rw-r--r--user_guide_src/source/libraries/encryption.rst555
3 files changed, 563 insertions, 6 deletions
diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php
index e03e278fa..3a5409839 100644
--- a/system/libraries/Encryption.php
+++ b/system/libraries/Encryption.php
@@ -698,7 +698,7 @@ class CI_Encryption {
'mode' => $params['mode'],
'key' => $params['key'],
'iv' => isset($params['iv']) ? $params['iv'] : NULL,
- 'base64' => isset($params['base64']) ? $params['base64'] : TRUE,
+ 'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,
'hmac_digest' => $params['hmac_digest'],
'hmac_key' => $params['hmac_key']
);
@@ -825,8 +825,8 @@ class CI_Encryption {
* @param $key Input key
* @param $digest A SHA-2 hashing algorithm
* @param $salt Optional salt
- * @param $info Optional context/application-specific info
* @param $length Output length (defaults to the selected digest size)
+ * @param $info Optional context/application-specific info
* @return string A pseudo-random key
*/
public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
diff --git a/tests/codeigniter/libraries/Encryption_test.php b/tests/codeigniter/libraries/Encryption_test.php
index 6bcf17b14..54db2b42d 100644
--- a/tests/codeigniter/libraries/Encryption_test.php
+++ b/tests/codeigniter/libraries/Encryption_test.php
@@ -151,20 +151,22 @@ class Encryption_test extends CI_TestCase {
'mode' => 'cbc',
'key' => str_repeat("\x0", 16),
'iv' => str_repeat("\x0", 16),
- 'base64' => FALSE,
+ 'raw_data' => TRUE,
'hmac_key' => str_repeat("\x0", 16),
'hmac_digest' => 'sha256'
);
$output = $this->encryption->__get_params($params);
- unset($output['handle']);
+ unset($output['handle'], $params['raw_data']);
+ $params['base64'] = FALSE;
$this->assertEquals($params, $output);
// HMAC disabled
unset($params['hmac_key'], $params['hmac_digest']);
- $params['hmac'] = FALSE;
+ $params['hmac'] = $params['raw_data'] = FALSE;
$output = $this->encryption->__get_params($params);
- unset($output['handle'], $params['hmac']);
+ unset($output['handle'], $params['hmac'], $params['raw_data']);
+ $params['base64'] = TRUE;
$params['hmac_digest'] = $params['hmac_key'] = NULL;
$this->assertEquals($params, $output);
}
diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst
new file mode 100644
index 000000000..cedc8d381
--- /dev/null
+++ b/user_guide_src/source/libraries/encryption.rst
@@ -0,0 +1,555 @@
+##################
+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('encrypt');
+
+Once loaded, the Encryption library object will be available using::
+
+ $this->encrypt
+
+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 40-128 / 5-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-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:: 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.
+iv N/A No Initialization vector (IV).
+ 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 \ No newline at end of file