Permalink
Browse files

Encapsulate Crypto calls, add header to detect if it's a legacy or ne…

…w encryption.
  • Loading branch information...
1 parent aaf0ec8 commit b745b30d25e1293d9515eef38bcc2f07cdf99991 @cdujeu cdujeu committed Sep 26, 2016
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Copyright 2007-2016 Abstrium <contact (at) pydio.com>
+ * This file is part of Pydio.
+ *
+ * Pydio 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.
+ *
+ * Pydio 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 Pydio. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The latest code can be found at <https://pydio.com/>.
+ */
+namespace Pydio\Tests\Atomics;
+
+use Pydio\Core\Utils\Crypto;
+
+defined('AJXP_EXEC') or die('Access not allowed');
+
+/**
+ * Class Crypto
+ * @package Pydio\Tests\Atomics
+ */
+class CryptoTests extends \PHPUnit_Framework_TestCase
+{
+ public function testEncryptDecrypt(){
+ $key = "test";
+ $data = "toto";
+ $encoded = Crypto::encrypt($data, Crypto::buildKey($key, Crypto::getApplicationSecret()));
+ $decoded = Crypto::decrypt($encoded, Crypto::buildKey($key, Crypto::getApplicationSecret()));
+ $this->assertTrue($data === $decoded);
+ }
+}
@@ -76,7 +76,7 @@ public static function applyActionInBackground(ContextInterface $ctx, $actionNam
$logFile = $logDir . "/" . $token . ".out";
if (UsersService::usersEnabled()) {
- $user = Crypto::encrypt($user, md5($token . Crypto::getCliSecret()));
+ $user = Crypto::encrypt($user, Crypto::buildKey($token , Crypto::getCliSecret()));
}
$robustInstallPath = str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH);
$cmd = ConfService::getGlobalConf("CLI_PHP") . " " . $robustInstallPath . DIRECTORY_SEPARATOR . "cmd.php -u=$user -t=$token -a=$actionName -r=$repositoryId";
@@ -70,7 +70,8 @@ protected static function authenticateFromCliParameters($options){
} else {
// Consider "u" is a crypted version of u:p
$optToken = $options["t"];
- $optUser = Crypto::decrypt($optUser, md5($optToken.Crypto::getCliSecret()));
+ $key = Crypto::buildKey($optToken, Crypto::getCliSecret());
+ $optUser = Crypto::decrypt($optUser, $key);
$envPass = MemorySafe::loadPasswordStringFromEnvironment($optUser);
if($envPass !== false){
unset($optToken);
@@ -111,49 +112,6 @@ public static function handleRequest(ServerRequestInterface $requestInterface, R
$optRepoId = $options["r"];
$impersonateUsers = false;
- // TODO 1/ REIMPLEMENT parameter queue: to pass a file with many user names?
- /**
- * if (strpos($optUser, "queue:") === 0) {
- $optUserQueue = substr($optUser, strlen("queue:"));
- $optUser = false;
- //echo("QUEUE : ".$optUserQueue);
- if (is_file($optUserQueue)) {
- $lines = file($optUserQueue);
- if (count($lines) && !empty($lines[0])) {
- $allUsers = explode(",", $lines[0]);
- $optUser = array_shift($allUsers);
- file_put_contents($optUserQueue, implode(",", $allUsers));
- }
- }
- if ($optUser === false) {
- if (is_file($optUserQueue)) {
- unlink($optUserQueue);
- }
- die("No more users inside queue");
- }
- */
- // TODO 2/ REIMPLEMENT DETECT USER PARAMETER BASED ON REPOSITORY PATH OPTION ?
- /*
- if ($optDetectUser != false) {
- $path = $repository->getOption("PATH", true);
- if (strpos($path, "AJXP_USER") !== false) {
- $path = str_replace(
- array("AJXP_INSTALL_PATH", "AJXP_DATA_PATH", "/"),
- array(AJXP_INSTALL_PATH, AJXP_DATA_PATH, DIRECTORY_SEPARATOR),
- $path
- );
- $parts = explode("AJXP_USER", $path);
- if(count($parts) == 1) $parts[1] = "";
- $first = str_replace("\\", "\\\\", $parts[0]);
- $last = str_replace("\\", "\\\\", $parts[1]);
- */
- //if (preg_match("/$first(.*)$last.*/", $optDetectUser, $matches)) {
- // $detectedUser = $matches[1];
- // }
- //}
- //}
-
-
$loggedUser = self::authenticateFromCliParameters($options);
$requestInterface = $requestInterface->withAttribute("action", $options["a"]);
@@ -182,7 +182,8 @@ protected function updateCurrentUserRights($user)
*/
private function _decodePassword($encoded, $user)
{
- return Crypto::decrypt($encoded, md5($user . $this->secretKey));
+ $key = Crypto::buildKey($user, Crypto::getApplicationSecret(), $encoded);
+ return Crypto::decrypt($encoded, $key);
}
@@ -22,6 +22,7 @@
use phpseclib\Crypt\Rijndael;
use Pydio\Core\Services\ConfService;
+use Pydio\Core\Utils\Crypto\Key;
use Pydio\Core\Utils\Crypto\ZeroPaddingRijndael;
use Pydio\Core\Utils\Vars\StringHelper;
@@ -35,6 +36,7 @@
*/
class Crypto
{
+ const HEADER_CBC_128 = 'cbc-128';
/**
* @return string
@@ -62,32 +64,83 @@ public static function getCliSecret(){
* @param bool $base64encode
* @return string
*/
- public static function getRandomSalt($base64encode = true){
+ public static function getRandomSalt($base64encode = true, $size = 32){
if(function_exists('openssl_random_pseudo_bytes')){
- $salt = openssl_random_pseudo_bytes(32);
+ $salt = openssl_random_pseudo_bytes($size);
}else if (function_exists('mcrypt_create_iv')){
$salt = mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM);
}else{
- $salt = StringHelper::generateRandomString(32, true);
+ $salt = StringHelper::generateRandomString($size, true);
}
return ($base64encode ? base64_encode($salt) : $salt);
}
/**
+ * @return string
+ */
+ protected static function getDataHeader(){
+ return substr(md5(self::HEADER_CBC_128), 0, 16);
+ }
+
+ /**
+ * @param $data
+ * @return bool
+ */
+ public static function hasCBCEnctypeHeader($data){
+ $h = self::getDataHeader();
+ return (strpos($data, $h) === 0);
+ }
+
+ /**
+ * @param $data
+ * @return bool
+ */
+ private static function removeCBCEnctypeHeader(&$data){
+ $h = self::getDataHeader();
+ if(strpos($data, $h) === 0){
+ $data = substr($data, strlen($h));
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ /**
+ * Builds a key using various methods depending on legacy status or not
+ * @param string $userKey
+ * @param string $secret
+ * @param null $encodedData
+ * @return array|bool|string
+ */
+ public static function buildKey($userKey, $secret, $encodedData = null){
+ if($encodedData === null){
+ // new encryption, use new method
+ return Key::create($userKey.$secret);
+ }else if(self::hasCBCEnctypeHeader($encodedData)){
+ // New method detected
+ return Key::create($userKey . $secret, Key::STRENGTH_MEDIUM);
+ }else{
+ // Legacy
+ return Key::createLegacy($userKey . $secret);
+ }
+ }
+
+ /**
* @param mixed $data
* @param string $key
* @param bool $base64encode
* @return mixed
*/
public static function encrypt($data, $key, $base64encode = true){
- $r = new ZeroPaddingRijndael(Rijndael::MODE_ECB);
+ // Encrypt in new mode, prepending a fixed header to the encoded data.
+ $r = new ZeroPaddingRijndael(Rijndael::MODE_CBC);
$r->setKey($key);
- $r->setBlockLength(256);
+ $r->setBlockLength(128);
$encoded = $r->encrypt($data);
if($base64encode) {
- return base64_encode($encoded);
+ return self::getDataHeader().base64_encode($encoded);
} else {
- return $encoded;
+ return self::getDataHeader().$encoded;
}
}
@@ -98,12 +151,19 @@ public static function encrypt($data, $key, $base64encode = true){
* @return mixed
*/
public static function decrypt($data, $key, $base64encoded = true){
+ $test = self::removeCBCEnctypeHeader($data);
if($base64encoded){
$data = base64_decode($data);
}
- $r = new ZeroPaddingRijndael(Rijndael::MODE_ECB);
+ if($test){
+ $r = new ZeroPaddingRijndael(Rijndael::MODE_CBC);
+ $r->setBlockLength(128);
+ }else{
+ // Legacy encoding
+ $r = new ZeroPaddingRijndael(Rijndael::MODE_ECB);
+ $r->setBlockLength(256);
+ }
$r->setKey($key);
- $r->setBlockLength(256);
return $r->decrypt($data);
}
@@ -0,0 +1,101 @@
+<?php
+/*
+ * Copyright 2007-2016 Abstrium <contact (at) pydio.com>
+ * This file is part of Pydio.
+ *
+ * Pydio 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.
+ *
+ * Pydio 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 Pydio. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The latest code can be found at <https://pydio.com/>.
+ */
+namespace Pydio\Core\Utils\Crypto;
+
+use Pydio\Core\Utils\Crypto;
+
+defined('AJXP_EXEC') or die('Access not allowed');
+
+/**
+ * Class Key
+ * @package Pydio\Core\Utils\Crypto
+ */
+class Key
+{
+ const STRENGTH_LOW = 0;
+ const STRENGTH_MEDIUM = 1;
+ const STRENGTH_HIGH = 2;
+
+ const SIZE_128 = 16;
+ const SIZE_256 = 32;
+
+ /**
+ * @param $password
+ * @param int $strength
+ * @param null $options
+ * @return array|bool|string
+ */
+ public static function create($password, $strength = Key::STRENGTH_LOW, $options = null){
+
+ if(!$options){
+ $options = array(
+ "strength" => self::STRENGTH_MEDIUM,
+ "size" => self::SIZE_256,
+ "iterations" => 20000,
+ "salt" => Crypto::getRandomSalt(self::SIZE_256),
+ "hash_function" => "SHA512"
+ );
+ }
+
+ if($strength == self::STRENGTH_HIGH && function_exists('openssl_random_pseudo_bytes')){
+
+ $aes_key = self::create($password);
+ $method = "aes-" . strlen($options["size"]) . "-cbc";
+
+ $key = openssl_random_pseudo_bytes($options["size"]);
+ $rsa = openssl_pkey_new(array(
+ "digest_algo" => "sha512",
+ "private_key_bits" => "4096",
+ "private_key_type" => OPENSSL_KEYTYPE_RSA
+ ));
+ openssl_pkey_export($rsa, $private);
+
+ $iv = openssl_random_pseudo_bytes(16);
+ $private = openssl_encrypt($private, $method, $aes_key, OPENSSL_RAW_DATA, $iv);
+ $public = openssl_pkey_get_details($rsa)["key"];
+
+ $options["public"] = $public;
+ $options["private"] = $private;
+ $options["iv"] = $iv;
+ openssl_public_encrypt($key, $options["key"], $public);
+
+ return array(
+ $key,
+ $options
+ );
+
+ } else if($strength == self::STRENGTH_LOW){
+ return substr(hash($options["hash_function"], $password), 0, $options["size"]);
+
+ } else {
+ return openssl_pbkdf2($password, $options["salt"], $options["size"], $options["iterations"], $options["hash_function"]);
+ }
+ }
+
+ /**
+ * @param $password
+ * @return string
+ */
+ public static function createLegacy($password){
+ return md5($password);
+ }
+
+}
Oops, something went wrong.

0 comments on commit b745b30

Please sign in to comment.