-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Nikolay Dandanov
committed
Jan 11, 2018
1 parent
d83828d
commit 03d3bbd
Showing
3 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,7 @@ | ||
# firefly-iii-reencrypt-database | ||
This repository contains the necessary PHP and Bash scripts to re-encrypt (most of) the contents of the Firefly III database. | ||
|
||
Please edit the `reencrypt.sh` script with the database connection data of your Firefly III installation. | ||
Then, run the script, e.g. with `/bin/bash reencrypt.sh`. | ||
|
||
You need to have `PHP` installed, but you probably have it already, if you used to run Firefly III on the same machine. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
<?php | ||
/* | ||
* Quick 'n Dirty Laravel 5.1 decrypter. | ||
* | ||
* This is a slightly modified Laravel Crypt encryptor/decryptor script, | ||
* original by: | ||
* https://gist.github.com/leonjza/ce27aa7435f8d131d93f | ||
* Modifications done by Nikolay Dandanov | ||
* | ||
* Based directly off the source code at: | ||
* https://github.com/laravel/framework/blob/5.1/src/Illuminate/Encryption/Encrypter.php | ||
* | ||
* Have access to an application key from a .env? | ||
* Have some encrypted data you want to decrypt? | ||
* Well: (new Crypt($key))->decrypt($payload); should have you sorted | ||
* | ||
* Have access to an application key from a .env? | ||
* Want to replace an encrypted value? | ||
* Well: (new Crypt($key))->encrypt($payload); should have you sorted | ||
*/ | ||
|
||
//$payload = 'eyJpdiI6Im55MXhqZUkwdW5PYTVycmFFbUVLTnc9PSIsInZhbHVlIjoiQXptOGxcLzVvZVFXUlwvaCtuXC9ad3BqZz09IiwibWFjIjoiMjE2MGU3NjliODVmYWNmNzc1ZjNjY2FjODBkMmIxOWFkZWUxMDE0OWIzNzVjZGIwZDM1NzdiMmY5MmY2NjllNSJ9'; | ||
//$key = 'AvDs2rPepOLpWgypXPs04JCFWs4wbDTe'; | ||
|
||
$payload = $argv[1]; | ||
$key = $argv[2]; | ||
$newkey = $argv[3]; | ||
|
||
|
||
/* | ||
echo $payload; | ||
echo " | ||
"; | ||
echo $key; | ||
echo " | ||
"; | ||
echo $newkey; | ||
echo " | ||
"; | ||
*/ | ||
|
||
$decrypted_data = (new Crypt($key))->decrypt($payload); | ||
$reencrypted_data = (new Crypt($newkey))->encrypt($decrypted_data); | ||
|
||
// Output | ||
$arr = array('decrypted_data' => $decrypted_data, 'reencrypted_data' => $reencrypted_data); | ||
echo json_encode($arr); | ||
|
||
|
||
/** | ||
* Class Crypt | ||
*/ | ||
class Crypt | ||
{ | ||
|
||
/** | ||
* The key used to decrypt. | ||
* | ||
* @var string | ||
*/ | ||
public $key; | ||
|
||
/** | ||
* The original encrypted payload | ||
* | ||
* @var string | ||
*/ | ||
public $payload; | ||
|
||
/** | ||
* The cipher types to try for decryption. | ||
* | ||
* @var array | ||
*/ | ||
protected $ciphers = [ | ||
'AES-128-CBC', | ||
'AES-256-CBC' | ||
]; | ||
|
||
/** | ||
* Construct a new Crypt | ||
* | ||
* @param null $key | ||
*/ | ||
public function __construct($key = null) | ||
{ | ||
|
||
if (!is_null($key)) | ||
$this->key = $key; | ||
} | ||
|
||
/** | ||
* Attempt to decrypt a payload. | ||
* | ||
* @param $payload | ||
* | ||
* @return mixed|string | ||
* @throws \Exception | ||
*/ | ||
public function decrypt($payload) | ||
{ | ||
|
||
$payload = json_decode(base64_decode($payload), true); | ||
|
||
if ($this->invalidPayload($payload)) | ||
throw new Exception('Invalid Payload Format. Want {iv, value, mac} json.'); | ||
|
||
$iv = base64_decode($payload['iv']); | ||
|
||
foreach ($this->ciphers as $cipher) { | ||
|
||
$decrypted = openssl_decrypt($payload['value'], | ||
$cipher, $this->key, 0, $iv); | ||
|
||
if ($decrypted) | ||
return unserialize($decrypted); | ||
} | ||
|
||
return 'Failed to decrypt the payload.'; | ||
} | ||
|
||
/** | ||
* Encrypt a payload. | ||
* | ||
* @param $payload | ||
* @param string $cipher | ||
* @param int $length | ||
* | ||
* @return string | ||
*/ | ||
public function encrypt($payload, $cipher = 'AES-256-CBC', $length = 16) | ||
{ | ||
|
||
$iv = $bytes = openssl_random_pseudo_bytes($length, $strong); | ||
$value = openssl_encrypt(serialize($payload), $cipher, $this->key, 0, $iv); | ||
$mac = $this->hmac($iv = base64_encode($iv), $value, $this->key); | ||
|
||
return base64_encode(json_encode(compact('iv', 'value', 'mac'))); | ||
|
||
} | ||
|
||
/** | ||
* Generate a sha256 keyed hash value. | ||
* | ||
* @param $iv | ||
* @param $value | ||
* @param $key | ||
* | ||
* @return string | ||
*/ | ||
public function hmac($iv, $value, $key) | ||
{ | ||
|
||
return hash_hmac('sha256', $iv . $value, $key); | ||
} | ||
|
||
/** | ||
* Check if a payload is in the correct format. | ||
* | ||
* @param $data | ||
* | ||
* @return bool | ||
*/ | ||
public function invalidPayload($data) | ||
{ | ||
|
||
return !is_array($data) || !isset($data['iv']) || | ||
!isset($data['value']) || !isset($data['mac']); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#!/bin/bash | ||
|
||
# Configuration | ||
# Caution: use secure credentials! | ||
# Change any names where appropriate | ||
db_user="root" | ||
db_pass="123456" | ||
db_name="firefly_db" | ||
|
||
# Declare the arrays, which contain the fields and tables to be optimized | ||
declare -a fields=("name" "name" "match" "name" "data" "tag" "description" "description"); | ||
declare -a tables=("accounts" "bills" "bills" "categories" "preferences" "tags" "tags" "transaction_journals" ); | ||
# The above means, that all records for the column "name" from table "accounts" will be processed first. | ||
# Then, all records for the column "name" from table "match" will be processed. | ||
# And so on. | ||
|
||
# Original fields I used: | ||
#declare -a fields=("name" "iban" "name" "match" "name" "data" "tag" "description" "description"); | ||
#declare -a tables=("accounts" "accounts" "bills" "bills" "categories" "preferences" "tags" "tags" "transaction_journals" ); | ||
|
||
# The numbers of fields and tables records to iterate over is: | ||
fields_num=${#fields[@]}; | ||
tables_num=${#tables[@]}; | ||
if [ $fields_num -ne $tables_num ] | ||
then | ||
echo "The numbers of fields and tables records to iterate over do not match!" | ||
fi | ||
|
||
# The original key, with which the database records are encrypted. | ||
key="SomeRandomStringOf32CharsExactly"; | ||
|
||
# The new key, encoded with base64 | ||
# (just as supplied from the .env file in the new Firefly III installation) | ||
encodedNewKey="TXlOZXdSYW5kb21TdHJpbmdPZjMyQ2hhckV4YWN0bHkK"; | ||
# Decode the new key | ||
newKey=`echo -n $encodedNewKey | base64 --decode`; | ||
|
||
# Process the data | ||
# Get the data from the database | ||
for (( c=0; c<$fields_num; c++ )) | ||
do | ||
echo "Beginning to reencrypt the $[$c+1]. field-table combination:" | ||
echo "field is ${fields[$c]}" | ||
echo "table is ${tables[$c]}" | ||
i=0; | ||
# Reencrypt row by row | ||
while read -a row | ||
do | ||
encryptedData[$i]=$row; | ||
|
||
# Reencrypt the encryptedData with the new key | ||
# Here we use a slightly modified Laravel Crypt encryptor/decryptor script, | ||
# original by: | ||
# https://gist.github.com/leonjza/ce27aa7435f8d131d93f | ||
phpOut=`php crypt.php ${encryptedData[$i]} $key $newKey`; | ||
|
||
# Parse the PHP JSON output | ||
decryptedData=`echo -n $phpOut | jq --raw-output '.decrypted_data'` | ||
reencryptedData=`echo -n $phpOut | jq --raw-output '.reencrypted_data'` | ||
|
||
# Debug info: | ||
#echo $decryptedData | ||
#echo $reencryptedData | ||
|
||
# Update the data in the database, in-place | ||
mysql -s -u $db_user -p$db_pass $db_name \ | ||
-e "UPDATE ${tables[$c]} SET \`${fields[$c]}\`='$reencryptedData' WHERE \`${fields[$c]}\`='${encryptedData[$i]}' ;" | ||
|
||
i=$[$i+1]; | ||
done < <(mysql -s -N -u $db_user -p$db_pass $db_name \ | ||
-e "SELECT \`${fields[$c]}\` FROM ${tables[$c]};") | ||
# For a remote database, you can use this: | ||
# mysql -s -N -h $srv_db_ip --port=$srv_db_port -u $srv_db_user -p$srv_db_pass $srv_db_name -e "SELECT $field FROM $table WHERE $condition;" | ||
done |