pssht  latest
SSH server library written in PHP
GCM.php
1 <?php
2 
3 /*
4 * This file is part of pssht.
5 *
6 * (c) François Poirotte <clicky@erebot.net>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 
13 
18 class GCM
19 {
21  protected $H;
22 
24  protected $cipher;
25 
27  protected $taglen;
28 
30  protected $table;
31 
32  public function __construct($cipher, $key, $taglen)
33  {
34  $logging = \Plop\Plop::getInstance();
35 
36  $this->cipher = mcrypt_module_open($cipher, null, 'ecb', null);
37  mcrypt_generic_init($this->cipher, $key, str_repeat("\x00", 16));
38  $this->taglen = $taglen;
39 
40  $logging->debug('Pre-computing GCM table');
41  $H = gmp_init(
42  bin2hex(mcrypt_generic($this->cipher, str_repeat("\x00", 16))),
43  16
44  );
45  $H = str_pad(gmp_strval($H, 2), 128, '0', STR_PAD_LEFT);
46  $R = gmp_init('E1000000000000000000000000000000', 16);
47 
48  $this->table = array();
49  for ($i = 0; $i < 16; $i++) {
50  $this->table[$i] = array();
51  for ($j = 0; $j < 256; $j++) {
52  $V = gmp_init(dechex($j) . str_repeat("00", $i), 16);
53  $Z = gmp_init(0);
54  for ($k = 0; $k < 128; $k++) {
55  // Compute Z_n+1
56  if ($H[$k]) {
57  $Z = gmp_xor($Z, $V);
58  }
59 
60  // Compute V_n+1
61  $odd = gmp_testbit($V, 0);
62  $V = gmp_div_q($V, 2);
63  if ($odd) {
64  $V = gmp_xor($V, $R);
65  }
66  }
67  $this->table[$i][$j] = pack('H*', str_pad(gmp_strval($Z, 16), 32, 0, STR_PAD_LEFT));
68  }
69  }
70  $logging->debug('Done pre-computing GCM table');
71  }
72 
73  public function __destruct()
74  {
75  mcrypt_generic_deinit($this->cipher);
76  }
77 
78  public static function inc($X, $n)
79  {
80  $s = gmp_strval($X, 2);
81  $s1 = (string) substr($s, 0, -$n);
82  $s = gmp_add(gmp_init(substr($s, -$n), 2), 1);
83  $s = gmp_mod($s, gmp_pow(2, $n));
84  $s2 = str_pad(gmp_strval($s, 2), $n, '0', STR_PAD_LEFT);
85  return gmp_init($s1 . $s2, 2);
86  }
87 
88  protected function ghash($X)
89  {
90  $Xn = str_split($X, 16);
91  $m = count($Xn);
92  if (strlen($Xn[$m - 1]) != 16) {
93  throw new \InvalidArgumentException();
94  }
95 
96  // Inline lookup.
97  $Y = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
98  $Y2 = $Y;
99  for ($i = 0; $i < $m; $i++) {
100  $res = $Y2;
101  $val = $Y ^ $Xn[$i];
102  for ($j = 0; $j < 16; $j++) {
103  $res = $res ^ $this->table[$j][ord($val[15 - $j])];
104  }
105  $Y = $res;
106  }
107  return $Y;
108  }
109 
110  protected function gctr($ICB, $X)
111  {
112  if ($X === '') {
113  return '';
114  }
115 
116  $Xn = str_split($X, 16);
117  $n = count($Xn);
118  $CB = array(1 => $ICB);
119  $Yn = array();
120  for ($i = 1; $i < $n; $i++) {
121  $CB[$i + 1] = static::inc($CB[$i], 32);
122 
123  $t = mcrypt_generic(
124  $this->cipher,
125  // Pad CB[i] to the block size (128 bits)
126  pack('H*', str_pad(gmp_strval($CB[$i], 16), 32, '0', STR_PAD_LEFT))
127  );
128  $t = gmp_xor(
129  gmp_init(bin2hex($Xn[$i - 1]), 16),
130  gmp_init(bin2hex($t), 16)
131  );
132  $Yn[$i] = pack('H*', str_pad(gmp_strval($t, 16), 32, '0', STR_PAD_LEFT));
133  }
134 
135  // Cipher
136  $t = mcrypt_generic(
137  $this->cipher,
138  // Pad CB[i] to the block size (128 bits)
139  pack('H*', str_pad(gmp_strval($CB[$n], 16), 32, '0', STR_PAD_LEFT))
140  );
141  // MSB Xn*
142  $t = str_pad(gmp_strval(gmp_init(bin2hex($t), 16), 16), 32, '0', STR_PAD_LEFT);
143  $nn = strlen($Xn[$n - 1]) << 1;
144  $t = substr($t, 0, $nn);
145  // Yn*
146  $t = gmp_xor(gmp_init(bin2hex($Xn[$n - 1]), 16), gmp_init($t, 16));
147  $Yn[$n] = pack('H*', str_pad(gmp_strval($t, 16), $nn, '0', STR_PAD_LEFT));
148  return implode('', $Yn);
149  }
150 
151  public function ae($IV, $P, $A)
152  {
154  $ivlen = strlen($IV);
155  if ($ivlen === 12) {
156  $J0 = $IV . "\x00\x00\x00\x01";
157  } else {
158  $s = (16 - ($ivlen % 16)) % 16;
159  $t = gmp_strval(gmp_init($ivlen << 3, 10), 16);
160  $J0 = $this->ghash(
161  $IV .
162  str_repeat("\x00", $s) .
163  pack('H*', str_pad($t, 32, '0', STR_PAD_LEFT))
164  );
165  }
166 
167  $J0 = gmp_init(bin2hex($J0), 16);
168  $C = $this->gctr(static::inc($J0, 32), $P);
169  $Cl = strlen($C);
170  $u = (16 - ($Cl % 16)) % 16;
171  $Al = strlen($A);
172  $v = (16 - ($Al % 16)) % 16;
173  $S = $this->ghash(
174  $A .
175  str_repeat("\x00", $v) .
176  $C .
177  str_repeat("\x00", $u) .
178  pack('H*', str_pad(gmp_strval(gmp_init($Al << 3, 10), 16), 16, '0', STR_PAD_LEFT)) .
179  pack('H*', str_pad(gmp_strval(gmp_init($Cl << 3, 10), 16), 16, '0', STR_PAD_LEFT))
180  );
181  $T = substr($this->gctr($J0, $S), 0, $this->taglen >> 3);
182  return array($C, $T);
183  }
184 
185  public function ad($IV, $C, $A, $T)
186  {
188  if (strlen($T) !== $this->taglen >> 3) {
189  return null;
190  }
191 
192  $ivlen = strlen($IV);
193  if ($ivlen === 12) {
194  $J0 = $IV . "\x00\x00\x00\x01";
195  } else {
196  $s = (16 - ($ivlen % 16)) % 16;
197  $t = gmp_strval(gmp_init($ivlen << 3, 10), 16);
198  $J0 = $this->ghash(
199  $IV .
200  str_repeat("\x00", $s) .
201  pack('H*', str_pad($t, 32, '0', STR_PAD_LEFT))
202  );
203  }
204 
205  $J0 = gmp_init(bin2hex($J0), 16);
206  $P = $this->gctr(static::inc($J0, 32), $C);
207  $Cl = strlen($C);
208  $u = (16 - ($Cl % 16)) % 16;
209  $Al = strlen($A);
210  $v = (16 - ($Al % 16)) % 16;
211  $S = $this->ghash(
212  $A .
213  str_repeat("\x00", $v) .
214  $C .
215  str_repeat("\x00", $u) .
216  pack('H*', str_pad(gmp_strval(gmp_init($Al << 3, 10), 16), 16, '0', STR_PAD_LEFT)) .
217  pack('H*', str_pad(gmp_strval(gmp_init($Cl << 3, 10), 16), 16, '0', STR_PAD_LEFT))
218  );
219  $T2 = substr($this->gctr($J0, $S), 0, $this->taglen >> 3);
220  if ($T !== $T) {
221  return null;
222  }
223  return $P;
224  }
225 }
$table
Pre-computation table for GF(2**128)
Definition: GCM.php:30
$H
Block H, the hash subkey.
Definition: GCM.php:21
$taglen
Output tag length.
Definition: GCM.php:27
$cipher
Approved block cipher with a 128-bit block size.
Definition: GCM.php:24