/
LSSodium.php
183 lines (168 loc) · 7.57 KB
/
LSSodium.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<?php
class LSSodium
{
public $bLibraryExists = false;
protected $sEncryptionKeypair = null;
protected $sEncryptionPublicKey = null;
protected $sEncryptionSecretKey = null;
public function init()
{
require_once Yii::app()->basePath . '/../third_party/paragonie/sodium_compat/src/Compat.php';
require_once Yii::app()->basePath . '/../third_party/paragonie/sodium_compat/src/Core/Util.php';
require_once Yii::app()->basePath . '/../third_party/paragonie/sodium_compat/autoload.php';
$this->checkIfLibraryExists();
if ($this->bLibraryExists === false) {
/*throw new SodiumException(sprintf(gT("This operation uses data encryption functions which require Sodium library to be installed, but library was not found. If you don't want to use data encryption, you have to disable encryption in attribute settings. Here is a link to the manual page:
%s", 'unescaped'), 'https://manual.limesurvey.org/Data_encryption#Errors'));*/
} else {
$this->checkIfKeyExists();
}
}
/**
* Check if Sodium library is installed
* @return bool
*/
public function checkIfLibraryExists()
{
if (function_exists('sodium_crypto_sign_open')) {
$this->bLibraryExists = true;
}
}
/**
*
* Check if encryption key exists in configuration
* @return bool Return decrypted value (string or unsezialized object) if suceeded. Return FALSE if an error occurs (bad password/salt given) or inpyt encryptedString
*/
protected function checkIfKeyExists()
{
if (empty(Yii::app()->getConfig('encryptionkeypair'))) {
$this->generateEncryptionKeys(); //return false;
}
if ($this->sEncryptionKeypair === null) {
$this->sEncryptionKeypair = $this->getEncryptionKey();
}
if ($this->sEncryptionPublicKey === null) {
$this->sEncryptionPublicKey = $this->getEncryptionPublicKey();
}
if ($this->sEncryptionSecretKey === null) {
$this->sEncryptionSecretKey = $this->getEncryptionSecretKey();
}
}
/**
*
* Get encryption key from version.php config file
* @return string Return encryption key string
*/
protected function getEncryptionKey()
{
return ParagonIE_Sodium_Compat::hex2bin(Yii::app()->getConfig('encryptionkeypair'));
}
/**
*
* Get encryption key from version.php config file
* @return string Return encryption key string
*/
protected function getEncryptionPublicKey()
{
return ParagonIE_Sodium_Compat::hex2bin(Yii::app()->getConfig('encryptionpublickey'));
}
/**
*
* Get encryption key from version.php config file
* @return string Return encryption key string
*/
protected function getEncryptionSecretKey()
{
return ParagonIE_Sodium_Compat::hex2bin(Yii::app()->getConfig('encryptionsecretkey'));
}
/**
* Encrypt input data using AES256 CBC encryption
* @param unknown_type $sDataToEncrypt Data to encrypt. Could be a string or a serializable PHP object
* @return string Return encrypted AES256 CBC value
*/
public function encrypt($sDataToEncrypt)
{
if ($this->bLibraryExists === true) {
if (!empty($sDataToEncrypt)) {
$sEncrypted = base64_encode(ParagonIE_Sodium_Compat::crypto_sign((string) $sDataToEncrypt, $this->sEncryptionSecretKey));
return $sEncrypted;
} else {
return '';
}
} else {
return $sDataToEncrypt;
}
}
/**
*
* Decrypt encrypted string.
* @param string $sEncryptedString Encrypted string to decrypt
* @param bool $bReturnFalseIfError false by default. If TRUE, return false in case of error (bad decryption). Else, return given $encryptedInput value
* @return string Return decrypted value (string or unsezialized object) if suceeded. Return FALSE if an error occurs (bad password/salt given) or inpyt encryptedString
*/
public function decrypt($sEncryptedString, $bReturnFalseIfError = false)
{
if ($this->bLibraryExists === true) {
if (!empty($sEncryptedString) && $sEncryptedString != 'null') {
$plaintext = ParagonIE_Sodium_Compat::crypto_sign_open(base64_decode($sEncryptedString), $this->sEncryptionPublicKey);
if ($plaintext === false) {
throw new SodiumException(sprintf(gT("Wrong decryption key! Decryption key has changed since this data were last saved, so data can't be decrypted. Please consult our manual at %s.", 'unescaped'), 'https://manual.limesurvey.org/Data_encryption#Errors'));
} else {
return $plaintext;
}
}
} else {
return $sEncryptedString;
}
}
/**
*
* Write encryption key to version.php config file
*/
protected function generateEncryptionKeys()
{
if (is_file(APPPATH . 'config/security.php')) {
// Never replace an existing file
throw new CException(500, gT("Configuration file already exist"));
}
$sEncryptionKeypair = ParagonIE_Sodium_Compat::crypto_sign_keypair();
$sEncryptionPublicKey = ParagonIE_Sodium_Compat::bin2hex(ParagonIE_Sodium_Compat::crypto_sign_publickey($sEncryptionKeypair));
$sEncryptionSecretKey = ParagonIE_Sodium_Compat::bin2hex(ParagonIE_Sodium_Compat::crypto_sign_secretkey($sEncryptionKeypair));
$sEncryptionKeypair = ParagonIE_Sodium_Compat::bin2hex($sEncryptionKeypair);
if (empty($sEncryptionKeypair)) {
return false;
}
$sConfig = "<?php if (!defined('BASEPATH')) exit('No direct script access allowed');" . "\n"
. "/*" . "\n"
. " * LimeSurvey" . "\n"
. " * Copyright (C) 2007-2019 The LimeSurvey Project Team / Carsten Schmitz" . "\n"
. " * All rights reserved." . "\n"
. " * License: GNU/GPL License v3 or later, see LICENSE.php" . "\n"
. " * LimeSurvey is free software. This version may have been modified pursuant" . "\n"
. " * to the GNU General Public License, and as distributed it includes or" . "\n"
. " * is derivative of works licensed under the GNU General Public License or" . "\n"
. " * other free or open source software licenses." . "\n"
. " * See COPYRIGHT.php for copyright notices and details." . "\n"
. " */" . "\n"
. "\n"
. "/* " . "\n"
. "WARNING!!!" . "\n"
. "ONCE SET, ENCRYPTION KEYS SHOULD NEVER BE CHANGED, OTHERWISE ALL ENCRYPTED DATA COULD BE LOST !!!" . "\n"
. "\n"
. "*/" . "\n"
. "\n"
. "\$config = array();" . "\n"
. "\$config['encryptionkeypair'] = '" . $sEncryptionKeypair . "';" . "\n"
. "\$config['encryptionpublickey'] = '" . $sEncryptionPublicKey . "';" . "\n"
. "\$config['encryptionsecretkey'] = '" . $sEncryptionSecretKey . "';" . "\n"
. "return \$config;";
Yii::app()->setConfig("encryptionkeypair", $sEncryptionKeypair);
Yii::app()->setConfig("encryptionpublickey", $sEncryptionPublicKey);
Yii::app()->setConfig("encryptionsecretkey", $sEncryptionSecretKey);
if (is_writable(APPPATH . 'config')) {
file_put_contents(APPPATH . 'config/security.php', $sConfig);
} else {
throw new CHttpException(500, gT("Configuration directory is not writable"));
}
}
}