Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikolay Dandanov committed Jan 11, 2018
1 parent d83828d commit 03d3bbd
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
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.
171 changes: 171 additions & 0 deletions crypt.php
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']);
}

}
74 changes: 74 additions & 0 deletions reencrypt.sh
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

0 comments on commit 03d3bbd

Please sign in to comment.