Completed
Push — master ( 04748f...585c72 )
by Marco
07:11
created

EncryptedCookie::encryptKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php namespace Comodojo\Cookies;
2
3
use \phpseclib\Crypt\AES;
4
use \Comodojo\Exception\CookieException;
5
6
/**
7
 * AES-encrypted cookie
8
 *
9
 * @package     Comodojo Spare Parts
10
 * @author      Marco Giovinazzi <[email protected]>
11
 * @license     MIT
12
 *
13
 * LICENSE:
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
 * THE SOFTWARE.
22
 */
23
24
class EncryptedCookie extends AbstractCookie {
25
26
    /*
27
     * AES key
28
     *
29
     * @var int
30
     */
31
    private $key = null;
32
33
    /**
34
     * Encrypted cookie constructor
35
     *
36
     * Setup cookie name and key
37
     *
38
     * @param   string   $name
39
     *
40
     * @param   string   $key
41
     *
42
     * @throws \Comodojo\Exception\CookieException
43
     */
44 42
    public function __construct($name, $key, $max_cookie_size = null) {
45
46 42
        if ( empty($key) OR !is_scalar($key) ) throw new CookieException("Invalid secret key");
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
47
48 42
        parent::__construct($name, $max_cookie_size);
49
50 42
    }
51
52
    /**
53
     * Set cookie content
54
     *
55
     * @param   mixed   $value      Cookie content
56
     * @param   bool    $serialize  If true (default) cookie will be serialized first
57
     *
58
     * @return  \Comodojo\Cookies\EncryptedCookie
59
     *
60
     * @throws  \Comodojo\Exception\CookieException
61
     */
62 24
    public function setValue($value, $serialize = true) {
63
64 24
        if ( !is_scalar($value) && $serialize === false ) throw new CookieException("Cannot set non-scalar value without serialization");
65
66 24
        if ( $serialize === true ) $value = serialize($value);
67
68 24
        $cipher = new AES(AES::MODE_ECB);
69
70 24
        $cipher->setKeyLength(256);
71
72 24
        $cipher->setKey(self::encryptKey($this->key));
73
74
        // added base64 encoding to avoid problems with binary data
75
76 24
        $cookie_value = base64_encode($cipher->encrypt($value));
77
78 24 View Code Duplication
        if ( strlen($cookie_value) > $this->max_cookie_size ) throw new CookieException("Cookie size larger than ".$this->max_cookie_size." bytes");
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
79
80 18
        $this->value = $cookie_value;
81
82 18
        return $this;
83
84
    }
85
86
    /**
87
     * Get cookie content
88
     *
89
     * @param   bool    $unserialize    If true (default) cookie will be unserialized first
90
     *
91
     * @return  mixed
92
     */
93 18
    public function getValue($unserialize = true) {
94
95 18
        $cipher = new AES(AES::MODE_ECB);
96
97 18
        $cipher->setKeyLength(256);
98
99 18
        $cipher->setKey(self::encryptKey($this->key));
100
101
        // added base64 encoding to avoid problems with binary data
102
103 18
        $encoded_cookie = base64_decode($this->value);
104
105 18
        if ( $encoded_cookie === false ) throw new CookieException("Cookie data cannot be decoded");
106
107 18
        $cookie = $cipher->decrypt($encoded_cookie);
108
109 18
        if ( $cookie === false ) throw new CookieException("Cookie data cannot be dectypted");
110
111 18
        return ($unserialize === true) ? unserialize($cookie) : $cookie;
112
113
    }
114
115
    /**
116
     * Static method to create a cookie quickly
117
     *
118
     * @param   string   $name  The cookie name
119
     *
120
     * @param   string   $key
121
     *
122
     * @param   array    $properties    Array of properties cookie should have
123
     *
124
     * @return  \Comodojo\Cookies\EncryptedCookie
125
     *
126
     * @throws  \Comodojo\Exception\CookieException
127
     */
128 6
    public static function create($name, $key, $properties = [], $serialize = true) {
129
130
        try {
131
132 6
            $class = get_called_class();
133
134 6
            $cookie = new $class($name, $key);
135
136 6
            self::cookieProperties($cookie, $properties, $serialize);
137
138 6
        } catch (CookieException $ce) {
139
140
            throw $ce;
141
142
        }
143
144 6
        return $cookie;
145
146
    }
147
148
    /**
149
     * Static method to get a cookie quickly
150
     *
151
     * @param   string   $name  The cookie name
152
     *
153
     * @param   string   $key
154
     *
155
     * @return  \Comodojo\Cookies\EncryptedCookie
156
     *
157
     * @throws  \Comodojo\Exception\CookieException
158
     */
159 6 View Code Duplication
    public static function retrieve($name, $key) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
161
        try {
162
163 6
            $class = get_called_class();
164
165 6
            $cookie = new $class($name, $key);
166
167 6
            $return = $cookie->load();
168
169 6
        } catch (CookieException $ce) {
170
171 6
            throw $ce;
172
173
        }
174
175
        return $return;
176
177
    }
178
179
    /**
180
     * Hash the key to generate a valid aes key value
181
     *
182
     * @param   string   $key
183
     *
184
     * @return  string
185
     */
186 24
    protected static function encryptKey($key) {
187
188 24
        return hash('sha256', $key);
189
190
    }
191
192
}
193