Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/passman.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorbrantje <brantje@gmail.com>2017-01-02 15:09:03 +0300
committerbrantje <brantje@gmail.com>2017-01-11 20:09:49 +0300
commite5c8c5d1f73a087a50f8f9a4a68f49909ef71769 (patch)
tree77fc4a0f81a1cde0ca0c612ab1b63a4911c55374 /lib
parent9469995a8fe45391e3801fded912555b92783dff (diff)
Create encryption class
Diffstat (limited to 'lib')
-rw-r--r--lib/Service/EncryptService.php272
1 files changed, 272 insertions, 0 deletions
diff --git a/lib/Service/EncryptService.php b/lib/Service/EncryptService.php
new file mode 100644
index 00000000..ea33abfa
--- /dev/null
+++ b/lib/Service/EncryptService.php
@@ -0,0 +1,272 @@
+<?php
+/**
+ * Nextcloud - passman
+ *
+ * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
+ * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Passman\Service;
+
+
+// Class copied from http://stackoverflow.com/questions/5089841/two-way-encryption-i-need-to-store-passwords-that-can-be-retrieved?answertab=votes#tab-top
+// Upgraded to use openssl
+class EncryptService {
+
+ /**
+ * Supported cipher algorithms accompanied by their key/block sizes in bytes
+ *
+ * OpenSSL has no equivalent of mcrypt_get_key_size() and mcrypt_get_block_size() hence sizes stored here.
+ *
+ * @var array
+ */
+ protected $supportedAlgos = array(
+ 'aes-256' => array('name' => 'AES-256', 'keySize' => 32, 'blockSize' => 32),
+ 'bf' => array('name' => 'BF', 'keySize' => 16, 'blockSize' => 8),
+ 'des' => array('name' => 'DES', 'keySize' => 7, 'blockSize' => 8),
+ 'des-ede3' => array('name' => 'DES-EDE3', 'keySize' => 21, 'blockSize' => 8), // 3 different 56-bit keys
+ 'cast5' => array('name' => 'CAST5', 'keySize' => 16, 'blockSize' => 8),
+ );
+
+ /**
+ * Supported encryption modes
+ *
+ * @var array
+ */
+ protected $supportedModes = array(
+ 'cbc' => 'CBC',
+ );
+
+ /**
+ * A class to handle secure encryption and decryption of arbitrary data
+ *
+ * Note that this is not just straight encryption. It also has a few other
+ * features in it to make the encrypted data far more secure. Note that any
+ * other implementations used to decrypt data will have to do the same exact
+ * operations.
+ *
+ * Security Benefits:
+ *
+ * - Uses Key stretching
+ * - Hides the Initialization Vector
+ * - Does HMAC verification of source data
+ *
+ */
+
+ /**
+ * Create an encryption key. Based on given parameters
+ *
+ * @param string $userKey The user key to use. This should be specific to this user.
+ * @param string $serverKey The server key
+ * @param string $userSuppliedKey A key from the credential (eg guid, name or tags)
+ * @return string
+ */
+
+ public static function makeKey($userKey, $serverKey, $userSuppliedKey) {
+ $key = hash_hmac('sha512', $userKey, $serverKey);
+ $key = hash_hmac('sha512', $key, $userSuppliedKey);
+ return $key;
+ }
+
+ /**
+ * @var string $cipher The mcrypt cipher to use for this instance
+ */
+ protected $cipher = '';
+
+ /**
+ * @var int $mode The mcrypt cipher mode to use
+ */
+ protected $mode = '';
+
+ /**
+ * @var int $rounds The number of rounds to feed into PBKDF2 for key generation
+ */
+ protected $rounds = 100;
+
+ /**
+ * Constructor!
+ *
+ * @param string $cipher The MCRYPT_* cypher to use for this instance
+ * @param int $mode The MCRYPT_MODE_* mode to use for this instance
+ * @param int $rounds The number of PBKDF2 rounds to do on the key
+ */
+ public function __construct($cipher, $mode, $rounds = 100) {
+ $this->cipher = $cipher;
+ $this->mode = $mode;
+ $this->rounds = (int)$rounds;
+ }
+
+ /**
+ * Get the maximum key size for the selected cipher and mode of operation
+ *
+ * @return int Value is in bytes
+ */
+ public function getKeySize() {
+ return $this->supportedAlgos[$this->cipher]['keySize'];
+ }
+
+ /**
+ * Decrypt the data with the provided key
+ *
+ * @param string $data_hex The encrypted datat to decrypt
+ * @param string $key The key to use for decryption
+ *
+ * @returns string|false The returned string if decryption is successful
+ * false if it is not
+ */
+ public function decrypt($data_hex, $key) {
+
+ if (!function_exists('hex2bin')) {
+ function hex2bin($str) {
+ $sbin = "";
+ $len = strlen($str);
+ for ($i = 0; $i < $len; $i += 2) {
+ $sbin .= pack("H*", substr($str, $i, 2));
+ }
+
+ return $sbin;
+ }
+ }
+
+ $data = hex2bin($data_hex);
+
+ $salt = substr($data, 0, 128);
+ $enc = substr($data, 128, -64);
+ $mac = substr($data, -64);
+
+ list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
+
+ if (!EncryptService::hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
+ return false;
+ }
+
+ $dec = openssl_decrypt($enc, $this->cipher . '-' . $this->mode, $cipherKey, true, $iv);
+ $data = $this->unpad($dec);
+
+ return $data;
+ }
+
+ /**
+ * Encrypt the supplied data using the supplied key
+ *
+ * @param string $data The data to encrypt
+ * @param string $key The key to encrypt with
+ *
+ * @returns string The encrypted data
+ */
+ public function encrypt($data, $key) {
+ $salt = openssl_random_pseudo_bytes(128);
+ list ($cipherKey, $macKey, $iv) = EncryptService::getKeys($salt, $key);
+ $data = EncryptService::pad($data);
+ $enc = openssl_encrypt($data, $this->cipher . '-' . $this->mode, $cipherKey, true, $iv);
+
+
+ $mac = hash_hmac('sha512', $enc, $macKey, true);
+
+ $data = bin2hex($salt . $enc . $mac);
+ return $data;
+
+ }
+
+ /**
+ * Generates a set of keys given a random salt and a master key
+ *
+ * @param string $salt A random string to change the keys each encryption
+ * @param string $key The supplied key to encrypt with
+ *
+ * @returns array An array of keys (a cipher key, a mac key, and a IV)
+ */
+ protected function getKeys($salt, $key) {
+ $ivSize = openssl_cipher_iv_length($this->cipher . '-' . $this->mode);
+ $keySize = openssl_cipher_iv_length($this->cipher . '-' . $this->mode);
+ $length = 2 * $keySize + $ivSize;
+
+ $key = EncryptService::pbkdf2('sha512', $key, $salt, $this->rounds, $length);
+
+ $cipherKey = substr($key, 0, $keySize);
+ $macKey = substr($key, $keySize, $keySize);
+ $iv = substr($key, 2 * $keySize);
+ return array($cipherKey, $macKey, $iv);
+ }
+
+ function hash_equals($a, $b) {
+ $key = openssl_random_pseudo_bytes(128);
+ return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
+ }
+
+ /**
+ * Stretch the key using the PBKDF2 algorithm
+ *
+ * @see http://en.wikipedia.org/wiki/PBKDF2
+ *
+ * @param string $algo The algorithm to use
+ * @param string $key The key to stretch
+ * @param string $salt A random salt
+ * @param int $rounds The number of rounds to derive
+ * @param int $length The length of the output key
+ *
+ * @returns string The derived key.
+ */
+ protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
+ $size = strlen(hash($algo, '', true));
+ $len = ceil($length / $size);
+ $result = '';
+ for ($i = 1; $i <= $len; $i++) {
+ $tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
+ $res = $tmp;
+ for ($j = 1; $j < $rounds; $j++) {
+ $tmp = hash_hmac($algo, $tmp, $key, true);
+ $res ^= $tmp;
+ }
+ $result .= $res;
+ }
+ return substr($result, 0, $length);
+ }
+
+ /**
+ * Pad the data with a random char chosen by the pad amount.
+ *
+ * @param $data
+ * @return string
+ */
+ protected function pad($data) {
+ $length = $this->getKeySize();
+ $padAmount = $length - strlen($data) % $length;
+ if ($padAmount == 0) {
+ $padAmount = $length;
+ }
+ return $data . str_repeat(chr($padAmount), $padAmount);
+ }
+
+
+ /**
+ * Unpad the the data
+ *
+ * @param $data
+ * @return bool|string
+ */
+ protected function unpad($data) {
+ $length = $this->getKeySize();
+ $last = ord($data[strlen($data) - 1]);
+ if ($last > $length) return false;
+ if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
+ return false;
+ }
+ return substr($data, 0, -1 * $last);
+ }
+} \ No newline at end of file