UrlCrypt::getInstance()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * URLCrypt
5
 *
6
 * PHP library to securely encode and decode short pieces of arbitrary binary data in URLs.
7
 *
8
 * (c) Guillermo Gonzalez
9
 *
10
 * For the full copyright and license information, please view the COPYING
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Atrapalo\UrlCrypt;
15
16
/**
17
 * Class UrlCrypt
18
 * @package Atrapalo\UrlCrypt
19
 */
20
class UrlCrypt
21
{
22
    public $table = "1bcd2fgh3jklmn4pqrstAvwxyz567890";
23
    private $ivSize;
24
    private $cipher = 'AES-128-CBC';
25
26
    public function __construct(string $table = null)
27
    {
28
        $this->ivSize = openssl_cipher_iv_length($this->cipher);
29
        if (!is_null($table) && $table != '') {
30
            $this->table = $table;
31
        }
32
    }
33 13
34
    public static function getInstance(string $table = null): UrlCrypt
35 13
    {
36 1
        return new self($table);
37 1
    }
38 13
39
    public function encode(string $string): string
40
    {
41
        $table = str_split($this->table, 1);
42
        $size = strlen($string) * 8 / 5;
43
        $stringArray = str_split($string, 1);
44 10
45
        $message = "";
46 10
        foreach ($stringArray as $char) {
47 10
            $message .= str_pad(decbin(ord($char)), 8, "0", STR_PAD_LEFT);
48 10
        }
49
50 10
        $message = str_pad($message, ceil(strlen($message) / 5) * 5, "0", STR_PAD_RIGHT);
51 10
52 10
        $encodeString = "";
53 10
        for ($i = 0; $i < $size; $i++) {
54
            $encodeString .= $table[bindec(substr($message, $i * 5, 5))];
55 10
        }
56
57 10
        return $encodeString;
58 10
    }
59 9
60 9
    public function decode(string $string): string
61
    {
62 10
        $table = str_split($this->table, 1);
63
        $size = strlen($string) * 5 / 8;
64
        $stringArray = str_split($string, 1);
65
66
        $message = "";
67
        foreach ($stringArray as $char) {
68
            $message .= str_pad(decbin(array_search($char, $table)), 5, "0", STR_PAD_LEFT);
69 10
        }
70
71 10
        $originalString = '';
72 10
        for ($i = 0; $i < floor($size); $i++) {
73 10
            $originalString .= chr(bindec(substr($message, $i * 8, 8)));
74
        }
75 10
76 10
        return $originalString;
77 10
    }
78 10
79
    public function encrypt(string $string, string $key): string
80 10
    {
81 10
        $key = $this->prepareKey($key);
82 9
        $iv = openssl_random_pseudo_bytes($this->ivSize);
83 9
        $cipherText = openssl_encrypt($string, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);
84
85 10
        if(false === $cipherText) {
86
            $this->throwOpensslError();
87
        }
88
89
        return $this->encode(base64_encode($cipherText).'::'.base64_encode($iv));
90
    }
91
92
    public function decrypt(string $string, string $key): string
93
    {
94 9
        $string = $this->decode($string);
95
96 9
        if (strpos($string, '::') === false) {
97 7
            return $this->legacyDecrypt($string, $key);
98 7
        }
99 7
100
        $key = $this->prepareKey($key);
101 7
        list($string, $iv) = explode('::', $string);
102
        $string = openssl_decrypt(base64_decode($string), $this->cipher, $key, OPENSSL_RAW_DATA, base64_decode($iv));
103
104
        return $string;
105
    }
106
107
    private function prepareKey(string $key): string
108
    {
109
        if (is_null($key) || $key == "") {
110 7
            throw new \Exception('No key provided.');
111
        }
112 7
113 7
        return md5($key, true);
114
    }
115 7
116 7
    private function isHexString(string $string): string
117 7
    {
118
        return (preg_match('/^[0-9a-f]+$/i', $string) === 1);
119 7
    }
120
121
    private function throwOpensslError()
122
    {
123
        $message = '';
124
        while ($msg = openssl_error_string()) {
125
            $message .= "{$msg}. ";
126
        }
127 9
128
        throw new \RuntimeException(trim($message));
129 9
    }
130 2
131
    private function legacyDecrypt(string $string, string $key): string
132
    {
133 7
        $key = $this->legacyPrepareKey($key);
134 3
        $iv = substr($string, 0, $this->ivSize);
135 4
        $string = substr($string, $this->ivSize);
136 1
        $string = openssl_decrypt($string, $this->cipher, $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
137
138 3
        preg_match_all('#([\\000]+)$#', $string, $matches);
139
        if (isset($matches[1][0]) && mb_strlen($matches[1][0], '8bit') > 1) {
140
            $string = rtrim($string, "\0");
141
        }
142
143
        return $string;
144
    }
145
146 3
    private function legacyPrepareKey(string $key): string
147
    {
148 3
        if (is_null($key) || $key == "") {
149
            throw new \Exception('No key provided.');
150
        }
151
152
        if (in_array(strlen($key), [32, 48, 64]) && $this->isHexString($key)) {
153
            return pack('H*', $key);
154
        } elseif (in_array(strlen($key), [16, 24, 32])) {
155
            return $key;
156
        } else {
157
            return md5($key);
158
        }
159
    }
160
}
161