Skip to content

Commit

Permalink
use libsodium for encryption if it's available
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonmunro committed Apr 11, 2017
1 parent 5926ab9 commit a536cca
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 7 deletions.
12 changes: 6 additions & 6 deletions lib/crypt.php
Expand Up @@ -54,13 +54,13 @@ public static function validate($key) {
}
}

class Hm_Crypt {
class Hm_Crypt_Base {

static private $method = 'aes-256-cbc';
static private $hmac = 'sha512';
static private $password_rounds = 86000;
static private $encryption_rounds = 100;
static private $hmac_rounds = 101;
static protected $method = 'aes-256-cbc';
static protected $hmac = 'sha512';
static protected $password_rounds = 86000;
static protected $encryption_rounds = 100;
static protected $hmac_rounds = 101;

/**
* Convert ciphertext to plaintext
Expand Down
108 changes: 108 additions & 0 deletions lib/crypt_sodium.php
@@ -0,0 +1,108 @@
<?php

/**
* Encryption with the libsodium extension
*
* This extends our standard encryption and will override it if libsodium is
* present. Checking a password and converting cipher text to clear text fall
* back to the standard Hm_Crypt library if they fail. This is to ensure
* backwards compatibility.
*
* @package framework
* @subpackage crypt
*/

class Hm_Crypt extends Hm_Crypt_Base {

/**
* Convert ciphertext to plaintext
* @param string $string ciphertext to decrypt
* @param string $key encryption key
* @return string decrypted text
*/
public static function plaintext($string, $key) {
if (!LIBSODIUM) {
return parent::plaintext($string, $key);
}
$res = false;
$raw_string = base64_decode($string);
if (!$raw_string || strlen($raw_string) < 60) {
return false;
}
list($salt, $crypt_key) = self::keygen($key, substr($raw_string, 0, 24));
$hmac = substr($raw_string, 24, 32);
$crypt_string = substr($raw_string, 56);

if (\Sodium\crypto_auth_verify($hmac, $crypt_string, $crypt_key)) {
$res = Sodium\crypto_secretbox_open($crypt_string, $salt, $crypt_key);
}
if ($res === false) {
return parent::plaintext($string, $key);
}
return $res;
}

/**
* Convert plaintext into ciphertext
* @param string $string plaintext to encrypt
* @param string $key encryption key
* @return string encrypted text
*/
public static function ciphertext($string, $key) {
if (!LIBSODIUM) {
return parent::ciphertext($string, $key);
}
list($salt, $key) = self::keygen($key);
$ciphertext = \Sodium\crypto_secretbox($string, $salt, $key);
$mac = \Sodium\crypto_auth($ciphertext, $key);
return base64_encode($salt.$mac.$ciphertext);
}

/**
* Hash a password using libsodium. Args not defined here are for backwards
* compat usage with our built in crypto
* @param string $password password to hash
* @return string
*/
public static function hash_password($password, $salt=false, $count=false, $algo='sha512', $type='php') {
if (!LIBSODIUM) {
return parent::hash_password($password, $salt, $count, $algo, $type);
}
return Sodium\crypto_pwhash_str( $password,
\Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
}

/**
* Check a password against it's stored hash using libsodium. If that
* fails, try our built in crypto otherwise updating to libsodium breaks
* everything
* @param string $password clear text password
* @param string $hash hashed password
* @return bool
*/
public static function check_password($password, $hash) {
if (!LIBSODIUM) {
return parent::check_password($password, $hash);
}
if (!\Sodium\crypto_pwhash_str_verify($hash, $password)) {
return parent::check_password($password, $hash);
}
return true;
}

/**
* stretch (or shrink) a key for libsodium
* @param string $key the key to stretch/shrink
* @param string $salt a salt to use, or create one if needed
* @return array
*/
private static function keygen($key, $salt=false) {
if (!$salt) {
$salt = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
}
return array($salt, parent::pbkdf2($key, $salt, 32, parent::$encryption_rounds, parent::$hmac));
}

}
2 changes: 1 addition & 1 deletion lib/format.php
Expand Up @@ -51,7 +51,7 @@ public function content($output, $allowed_output) {
$output['router_user_msgs'] = Hm_Msgs::get();
$output = $this->filter_output($output, $allowed_output);
if ($this->config->get('encrypt_ajax_requests', false)) {
$output = array('payload' => Hm_Crypt::ciphertext(json_encode($output, JSON_FORCE_OBJECT), Hm_Request_Key::generate()));
$output = array('payload' => Hm_Crypt_Base::ciphertext(json_encode($output, JSON_FORCE_OBJECT), Hm_Request_Key::generate()));
}
return json_encode($output, JSON_FORCE_OBJECT);
}
Expand Down
5 changes: 5 additions & 0 deletions lib/framework.php
Expand Up @@ -22,6 +22,7 @@
require APP_PATH.'lib/cache.php';
require APP_PATH.'lib/output.php';
require APP_PATH.'lib/crypt.php';
require APP_PATH.'lib/crypt_sodium.php';
require APP_PATH.'lib/db.php';
require APP_PATH.'lib/servers.php';
require APP_PATH.'lib/api.php';
Expand All @@ -30,6 +31,10 @@
require APP_PATH.'third_party/random_compat/lib/random.php';
}

if (!defined('LIBSODIUM')) {
define('LIBSODIUM', extension_loaded('libsodium'));
}

if (!class_exists('Hm_Functions')) {
/**
* Used to override built in functions that break unit tests
Expand Down

0 comments on commit a536cca

Please sign in to comment.