Completed
Push — master ( d18190...b4d042 )
by Florent
02:26
created

GCM::gcm_inc()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 8.8571
cc 5
eloc 11
nc 4
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
final class GCM
15
{
16
    /**
17
     * @param string $K
18
     * @param string $IV
19
     * @param string $P
20
     * @param string $A
21
     * @param int    $t
22
     *
23
     * @return array|null
24
     */
25
    public function gcm_encrypt($K, $IV, $P, $A, $t = 128) {
26
27
        $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
28
        if(!$cipher)
29
            return null;
30
        $key_length = StringUtil::getStringLength($K) * 8;
31
        if($key_length != 128 && $key_length != 192 && $key_length != 256) {
32
            die("encryp invalid key length {$key_length}\n");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_encrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
33
        }
34
35
        $iv_size = mcrypt_enc_get_iv_size($cipher);
0 ignored issues
show
Unused Code introduced by
$iv_size is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
36
        $iv = str_repeat(chr(0), 16);  // initialize to 16 byte string of "0"s
37
        $s = mcrypt_generic_init($cipher, $K, $iv);
38
        if( ($s < 0) || ($s === false)) {
39
            die("encryp mcrypt init error $s");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_encrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
40
        }
41
        $H = mcrypt_generic($cipher, StringUtil::addPadding('', 16, "\0"));
42
        $iv_len = $this->gcm_len($IV);
43
        if($iv_len == 96) {
44
            $J0 = $IV . pack('H*', '00000001');
45
        } else {
46
            $s = (128 * ceil($iv_len / 128)) - $iv_len;
47
            if(($s + 64) % 8)
48
                die("gcm_encrypt s {$s} + 64 not byte size");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_encrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
49
            $packed_iv_len = pack('N', $iv_len);
50
            $iv_len_padding = StringUtil::addPadding($packed_iv_len, 8, "\0", STR_PAD_LEFT);
51
            $hash_X = $IV . StringUtil::addPadding('', ($s + 64) / 8, "\0") . $iv_len_padding;
52
            $J0 = $this->gcm_hash($H, $hash_X);
53
        }
54
        $C = $this->gcm_gctr($K, $this->gcm_inc(32, $J0), $P);
55
56
        $u = (128 * ceil($this->gcm_len($C) / 128)) - $this->gcm_len($C);
57
        $v = (128 * ceil($this->gcm_len($A) / 128)) - $this->gcm_len($A);
58
        $a_len_padding = StringUtil::addPadding(pack('N', $this->gcm_len($A)), 8, "\0", STR_PAD_LEFT);
59
        $c_len_padding = StringUtil::addPadding(pack('N', $this->gcm_len($C)), 8, "\0", STR_PAD_LEFT);
60
61
        $S = $this->gcm_hash($H, $A . StringUtil::addPadding('', $v / 8, "\0") . $C . StringUtil::addPadding('', $u / 8, "\0") . $a_len_padding . $c_len_padding);
62
        $T = $this->gcm_MSB($t, $this->gcm_gctr($K, $J0, $S));
63
        mcrypt_generic_deinit($cipher);
64
        mcrypt_module_close($cipher);
65
        return array($C, $T);
66
    }
67
68
    /**
69
     * @param string $K
70
     * @param string $IV
71
     * @param string $C
72
     * @param string $A
73
     * @param string $T
74
     *
75
     * @return array|null
76
     */
77
    public function gcm_decrypt($K, $IV, $C, $A, $T) {
78
79
        $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
80
        if(!$cipher)
81
            return NULL;
82
        $key_length = StringUtil::getStringLength($K) * 8;
83
84
        if($key_length != 128 && $key_length != 192 && $key_length != 256) {
85
            die("encryp invalid key length\n");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_decrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
86
        }
87
88
        $iv_size = mcrypt_enc_get_iv_size($cipher);
0 ignored issues
show
Unused Code introduced by
$iv_size is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
89
        $iv = str_repeat(chr(0), 16);  // initialize to 16 byte string of "0"s
90
        $s = mcrypt_generic_init($cipher, $K, $iv);
91
        if( ($s < 0) || ($s === false)) {
92
            die("encryp mcrypt init error $s");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_decrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
93
        }
94
95
        $H = mcrypt_generic($cipher, StringUtil::addPadding('', 16, "\0"));
96
97
        $iv_len = $this->gcm_len($IV);
98
        if($iv_len == 96) {
99
            $J0 = $IV . pack('H*', '00000001');
100
        } else {
101
            $s = (128 * ceil($iv_len / 128)) - $iv_len;
102
            if(($s + 64) % 8)
103
                die("gcm_encrypt s {$s} + 64 not byte size");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_decrypt() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
104
            $packed_iv_len = pack('N', $iv_len);
105
            $iv_len_padding = StringUtil::addPadding($packed_iv_len, 8, "\0", STR_PAD_LEFT);
106
            $hash_X = $IV . StringUtil::addPadding('', ($s + 64) / 8, "\0") . $iv_len_padding;
107
            $J0 = $this->gcm_hash($H, $hash_X);
108
        }
109
        $P = $this->gcm_gctr($K, $this->gcm_inc(32, $J0), $C);
110
111
        $u = (128 * ceil($this->gcm_len($C) / 128)) - $this->gcm_len($C);
112
        $v = (128 * ceil($this->gcm_len($A) / 128)) - $this->gcm_len($A);
113
        $a_len_padding = StringUtil::addPadding(pack('N', $this->gcm_len($A)), 8, "\0", STR_PAD_LEFT);
114
        $c_len_padding = StringUtil::addPadding(pack('N', $this->gcm_len($C)), 8, "\0", STR_PAD_LEFT);
115
116
        $S = $this->gcm_hash($H, $A . StringUtil::addPadding('', $v / 8, "\0") . $C . StringUtil::addPadding('', $u / 8, "\0") . $a_len_padding . $c_len_padding);
117
        $T1 = $this->gcm_MSB($this->gcm_len($T), $this->gcm_gctr($K, $J0, $S));
118
        $result = strcmp($T, $T1);
119
        if($result)
120
            return null;
121
        mcrypt_generic_deinit($cipher);
122
        mcrypt_module_close($cipher);
123
        return $P;
124
    }
125
126
    /**
127
     * @param string $x
128
     *
129
     * @return int
130
     */
131
    private function gcm_len($x)
132
    {
133
        return StringUtil::getStringLength($x) * 8;
134
    }
135
136
    /**
137
     * @param int $num_bits
138
     * @param int $x
139
     *
140
     * @return string
141
     */
142
    private function gcm_MSB($num_bits, $x) {
143
        if(!$num_bits || !$x)
144
            die('gcm_MSB invalid params');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_MSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
145
        if($num_bits % 8)
146
            die('gcm_MSB num_bits is not byte size');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_MSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
147
        $num_bytes = $num_bits / 8;
148
        $len_x = StringUtil::getStringLength($x);
149
        if($num_bytes > StringUtil::getStringLength($x))
150
            die("gcm_MSB num_bits {$num_bits} bytes({$num_bytes}) > x {$len_x}");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_MSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
151
        return substr($x, 0, $num_bytes);
152
153
    }
154
155
    /**
156
     * @param int $num_bits
157
     * @param int $x
158
     *
159
     * @return string
160
     */
161
    private function gcm_LSB($num_bits, $x) {
162
        if(!$num_bits || !$x)
163
            die('gcm_LSB invalid params');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_LSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
164
        if($num_bits % 8)
165
            die('gcm_LSB num_bits is not byte size');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_LSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
166
        $num_bytes = ($num_bits / 8);
167
        if($num_bytes > StringUtil::getStringLength($x))
168
            die("gcm_LSB num_bits {$num_bits} > x {$x}");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_LSB() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
169
        return substr($x, $num_bytes * -1);
170
171
    }
172
173
    /**
174
     * @param int $s_bits
175
     * @param int $x
176
     *
177
     * @return string
178
     */
179
    private function gcm_inc($s_bits, $x) {
180
        if(!$s_bits || $s_bits != 32)
181
            die("gcm_inc invalid s_bits");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_inc() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
182
        if(!$x)
183
            die("gcm_inc invalid x");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_inc() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
184
        if($s_bits % 8)
185
            die('gcm_inc s_bits is not byte size');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_inc() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
186
        $lsb = $this->gcm_LSB($s_bits, $x);
187
        $X = ($this->_uint32be($lsb) + 1);
188
        $res = $this->gcm_MSB($this->gcm_len($x) - $s_bits, $x) . pack('N', $X);
189
        return $res;
190
    }
191
192
    /**
193
     * @param string $bin
194
     *
195
     * @return mixed
196
     */
197
    private function _uint32be($bin)
198
    {
199
        // $bin is the binary 32-bit BE string that represents the integer
200
//     $int_size = PHP_INT_SIZE;
201
        $int_size = 4;
202
        if ($int_size <= 4){
203
            list(,$h,$l) = unpack('n*', $bin);
204
            return ($l + ($h*0x010000));
205
        }
206
        else{
207
            list(,$int) = unpack('N', $bin);
208
            return $int;
209
        }
210
    }
211
212
    /**
213
     * @param $X
214
     * @param $Y
215
     *
216
     * @return string
217
     */
218
    private function gcm_product($X, $Y) {
219
        $R = pack('H*', 'E1') . StringUtil::addPadding('', 15, "\0");
220
        $Z = StringUtil::addPadding('', 16, "\0");
221
        $V = $Y;
222
        if(StringUtil::getStringLength($X) != 16)
223
            die('Invalid length for X');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_product() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
224
        $parts = str_split($X, 4);
225
        $x = sprintf("%032b%032b%032b%032b", $this->_uint32be($parts[0]), $this->_uint32be($parts[1]), $this->_uint32be($parts[2]), $this->_uint32be($parts[3]));
226
        $lsb_mask = "\1";
227
        for($i = 0; $i < 128; $i++) {
228
            if($x[$i])
229
                $Z = $this->bitxor($Z, $V);
230
            $lsb_8 = substr($V, -1);
231
            if(ord($lsb_8 & $lsb_mask))
232
                $V = $this->bitxor($this->str_right_shift($V), $R);
233
            else
234
                $V = $this->str_right_shift($V);
235
        }
236
        return $Z;
237
    }
238
239
    /**
240
     * @param string $input
241
     *
242
     * @return string
243
     */
244
    private function str_right_shift($input) {
245
//     $width = PHP_INT_SIZE; // doesn't work well on 64-bit systems
246
        $width = 4;
247
        $parts = array_map([$this, '_uint32be'], str_split($input, $width));
248
        $runs = count($parts);
249
        $len = StringUtil::getStringLength($input) / 4;
250
        if(!is_int($len))
251
            die('not int len');
0 ignored issues
show
Coding Style Compatibility introduced by
The method str_right_shift() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
252
        for($i=$runs - 1; $i >= 0; $i--) {
253
            if($i) {
254
                $lsb1 = $parts[$i - 1] & 0x00000001;
255
                if($lsb1) {
256
                    $parts[$i] = ($parts[$i] >> 1) | 0x80000000;
257
                    $parts[$i] = pack('N', $parts[$i]);
258
                    continue;
259
                }
260
            }
261
            $parts[$i] = ($parts[$i] >> 1) & 0x7FFFFFFF; // get rid of sign bit
262
            $parts[$i] = pack('N', $parts[$i]);
263
        }
264
        $res = implode('', $parts);
265
        return $res;
266
    }
267
268
    /**
269
     * @param string $H
270
     * @param string $X
271
     *
272
     * @return mixed
273
     */
274
    private function gcm_hash($H, $X) {
275
        if(!$H or !$X)
276
            die("gcm_hash invalid params");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_hash() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
277
        if(StringUtil::getStringLength($X) % 16)
278
            die("gcm_hash X is not multiple of 16 bytes");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_hash() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
279
        $Y = array();
280
        $Y[0] = StringUtil::addPadding('', 16, "\0");
281
        $num_blocks = StringUtil::getStringLength($X) / 16;
282
        for($i = 1; $i <= $num_blocks; $i++) {
283
            $Y[$i] = $this->gcm_product($this->bitxor($Y[$i - 1], substr($X, ($i - 1) * 16, 16)), $H);
284
        }
285
        return $Y[$num_blocks];
286
    }
287
288
    /**
289
     * @param string $K
290
     * @param string $ICB
291
     * @param string $X
292
     *
293
     * @return null|string
294
     */
295
    private function gcm_gctr($K, $ICB, $X) {
296
        if($X == '')
297
            return '';
298
299
        $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
300
        if(!$cipher)
301
            return NULL;
302
        $key_length = StringUtil::getStringLength($K) * 8;
303
304
        if($key_length != 128 && $key_length != 192 && $key_length != 256) {
305
            die("gcm_gctr invalid key length\n");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_gctr() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
306
        }
307
308
        $iv_size = mcrypt_enc_get_iv_size($cipher);
0 ignored issues
show
Unused Code introduced by
$iv_size is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
309
        $iv = str_repeat(chr(0), 16);  // initialize to 16 byte string of "0"s
310
        $s = mcrypt_generic_init($cipher, $K, $iv);
311
        if( ($s < 0) || ($s === false)) {
312
            die("gcm_gctr mcrypt init error $s");
0 ignored issues
show
Coding Style Compatibility introduced by
The method gcm_gctr() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
313
        }
314
315
        $n = ceil($this->gcm_len($X) / 128);
316
        $CB = array();
317
        $Y = array();
318
        $CB[1] = $ICB;
319
        for($i = 2; $i <= $n; $i++) {
320
            $CB[$i] = $this->gcm_inc(32, $CB[$i - 1]);
321
        }
322
        for($i = 1; $i < $n; $i++) {
323
            $C = mcrypt_generic($cipher, $CB[$i]);
324
            $Y[$i] = $this->bitxor(substr($X, ($i - 1) * 16, 16), $C);
325
        }
326
327
        $Xn = substr($X, ($n - 1) * 16);
328
        $C = mcrypt_generic($cipher, $CB[$n]);
329
        $Y[$n] = $this->bitxor($Xn, $this->gcm_MSB($this->gcm_len($Xn), $C));
330
        mcrypt_generic_deinit($cipher);
331
        mcrypt_module_close($cipher);
332
        return implode('', $Y);
333
    }
334
335
    /**
336
     * @param string $o1
337
     * @param string $o2
338
     *
339
     * @return string
340
     */
341
    private function bitxor($o1, $o2) {
342
        $xorWidth = PHP_INT_SIZE;
343
        $o1 = str_split($o1, $xorWidth);
344
        $o2 = str_split($o2, $xorWidth);
345
        $res = '';
346
        $runs = count($o1);
347
        for($i=0;$i<$runs;$i++)
348
            $res .= $o1[$i] ^ $o2[$i];
349
        return $res;
350
    }
351
}
352