Completed
Push — master ( ca77ed...3bcd04 )
by Michael
03:25
created

AesCtr::decrypt()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 30
rs 8.8571
cc 2
eloc 11
nc 2
nop 3
1
<?php
2
3
/**
4
 * AesCtr.php
5
 * 
6
 * PHP version 5
7
 * 
8
 * @category Dcrypt
9
 * @package  Dcrypt
10
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
11
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
12
 * @link     https://github.com/mmeyer2k/dcrypt
13
 */
14
15
namespace Dcrypt;
16
17
/**
18
 * Symmetric AES-256-CTR encryption functions powered by OpenSSL.
19
 * 
20
 * @category Dcrypt
21
 * @package  Dcrypt
22
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
23
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
24
 * @link     https://github.com/mmeyer2k/dcrypt
25
 * @link     https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
26
 */
27
class AesCtr extends Cryptobase
28
{
29
30
    /**
31
     * AES-256 cipher identifier that will be passed to openssl
32
     * 
33
     * @var string
34
     */
35
    const CIPHER = 'aes-256-ctr';
36
37
    /**
38
     * Size of initialization vector in bytes
39
     * 
40
     * @var int
41
     */
42
    const IVSIZE = 16;
43
44
    /**
45
     * Size of checksum in bytes
46
     * 
47
     * @var int
48
     */
49
    const CKSIZE = 32;
50
51
    /**
52
     * Decrypt cyphertext
53
     * 
54
     * @param string $cyphertext Cyphertext to decrypt
55
     * @param string $password   Password that should be used to decrypt input data
56
     * @param int    $cost       Number of HMAC iterations to perform on key
57
     * 
58
     * @return string|boolean Returns false on checksum validation failure
59
     */
60
    public static function decrypt($cyphertext, $password, $cost = 0)
61
    {
62
        // Find the IV at the beginning of the cypher text
63
        $iv = Str::substr($cyphertext, 0, self::IVSIZE);
64
65
        // Gather the key IV
66
        $keyiv = Str::substr($cyphertext, self::IVSIZE, self::IVSIZE);
67
68
        // Gather the checksum portion of the cypher text
69
        $chksum = Str::substr($cyphertext, self::IVSIZE * 2, self::CKSIZE);
70
71
        // Gather message portion of cyphertext after iv and checksum
72
        $message = Str::substr($cyphertext, self::IVSIZE * 2 + self::CKSIZE);
73
74
        // Derive key from password
75
        $key = self::key($password, $iv . $keyiv, $cost);
76
77
        // Calculate verification checksum
78
        $verify = self::checksum($message, $iv, $key);
79
80
        // Verify HMAC before decrypting
81
        if (!Str::equal($verify, $chksum)) {
82
            return static::invalidChecksum();
0 ignored issues
show
Bug introduced by
Since invalidChecksum() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of invalidChecksum() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
83
        }
84
85
        // Decrypt message with padding
86
        $padded = \openssl_decrypt($message, self::CIPHER, $key, 1, $iv);
87
88
        return Pkcs7::unpad($padded);
89
    }
90
91
    /**
92
     * Encrypt plaintext
93
     * 
94
     * @param string $plaintext Plaintext string to encrypt.
95
     * @param string $password  Password used to encrypt data.
96
     * @param int    $cost      Number of HMAC iterations to perform on key
97
     * 
98
     * @return string 
99
     */
100
    public static function encrypt($plaintext, $password, $cost = 0)
101
    {
102
        // Generate IV of appropriate size.
103
        $iv = Random::get(self::IVSIZE);
104
105
        // Generage a second IV used only for the key. This helps to mitigate
106
        // the problem of encrypting twice with the same IV and key.
107
        $keyiv = Random::get(self::IVSIZE);
108
109
        // Derive key from password
110
        $key = self::key($password, $iv . $keyiv, $cost);
111
112
        $padded = Pkcs7::pad($plaintext, 16);
113
114
        // Encrypt the plaintext
115
        $message = \openssl_encrypt($padded, self::CIPHER, $key, 1, $iv);
116
117
        // Create the cypher text prefix (iv + checksum)
118
        $prefix = $iv . $keyiv . self::checksum($message, $iv, $key);
119
120
        // Return prefix + cyphertext
121
        return $prefix . $message;
122
    }
123
124
    /**
125
     * By default, \Dcrypt\Aes will will return false when the checksum is invalid.
126
     * Use AesExp to force an exception to be thrown instead.
127
     * 
128
     * @return false
129
     */
130
    private static function invalidChecksum()
131
    {
132
        return false;
133
    }
134
135
}
136