27 const PRIME_128 =
'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61';
53 4 => MacEnum::MAC_UMAC_32(),
54 8 => MacEnum::MAC_UMAC_64(),
55 12 => MacEnum::MAC_UMAC_96(),
56 16 => MacEnum::MAC_UMAC_128(),
59 $taglen = array_search($macAlgorithm, $supported);
60 if (
false ===
$taglen || !extension_loaded(
'gmp')) {
61 throw new \InvalidArgumentException(
'Unsupported MAC algorithm');
64 if (!($innerAlgorithm instanceof
CipherEnum)) {
65 throw new \InvalidArgumentException(
'A cipher was expected as the inner algorithm');
69 throw new \InvalidArgumentException(
'Invalid key');
73 throw new \InvalidArgumentException(
'Invalid nonce');
76 $cipher = Registry::buildCipher($innerAlgorithm, ModeEnum::MODE_ECB(),
new None,
$key, 0,
true);
80 throw new \InvalidArgumentException(
'Incompatible cipher');
84 $this->subAlgo = $innerAlgorithm;
91 self::$twop32 = gmp_pow(2, 32);
92 self::$twop64 = gmp_pow(2, 64);
95 protected function internalUpdate($data)
100 protected function internalFinalize()
102 $hashed = $this->UHASH($this->data);
103 $pad = $this->PDF($this->nonce);
104 $tag = gmp_xor(gmp_init(bin2hex($pad), 16), gmp_init(bin2hex($hashed), 16));
105 $tag = gmp_strval($tag, 16);
106 $tag = pack(
'H*', str_pad($tag, $this->taglen << 1,
'0', STR_PAD_LEFT));
110 protected function KDF($index, $numbytes)
113 $n = (int) ceil($numbytes / $this->blkSize);
115 $bhex = (($this->blkSize - 8) << 1);
116 $pad =
"%0${bhex}X%016X";
119 for ($i = 1; $i <= $n; $i++) {
120 $t = pack(
'H*', sprintf($pad, $index, $i));
121 $t = $this->cipher->encrypt(
'', $t);
124 return substr($y, 0, $numbytes);
127 protected function PDF(
$nonce)
133 if ($this->taglen <= 8) {
134 $index = gmp_intval(gmp_mod(
$nonce, gmp_init($this->blkSize / $this->taglen)));
139 $nonce = pack(
'H*', str_pad(
$nonce, $nlen << 1,
'0', STR_PAD_LEFT));
144 $kprime = $this->KDF(0, strlen($this->key));
145 $cipher = Registry::buildCipher($this->subAlgo, ModeEnum::MODE_ECB(),
new None, $kprime, 0,
true);
148 if ($this->taglen <= 8) {
149 return substr($t, $index * $this->taglen, $this->taglen);
151 return substr($t, 0, $this->taglen);
155 protected function UHASH($m)
157 $iters = $this->taglen >> 2;
158 $l1key = $this->KDF(1, 1024 + ($iters - 1) << 4);
159 $l2key = $this->KDF(2, $iters * 24);
160 $l3key1 = $this->KDF(3, $iters * 64);
161 $l3key2 = $this->KDF(4, $iters * 4);
164 for ($i = 0; $i < $iters; $i++) {
165 $l1key_i = substr($l1key, $i << 4, 1024);
166 $l2key_i = substr($l2key, $i * 24, 24);
167 $l3key1_i = substr($l3key1, $i << 6, 64);
168 $l3key2_i = substr($l3key2, $i << 2, 4);
170 $a = $this->l1Hash($l1key_i, $m);
171 if (strlen($m) <= 1024) {
172 $b =
"\x00\x00\x00\x00\x00\x00\x00\x00" . $a;
174 $b = $this->l2Hash($l2key_i, $a);
176 $c = $this->l3Hash($l3key1_i, $l3key2_i, $b);
182 protected function l1Hash($k, $m)
185 $ms = str_split($m, 1024);
189 $len = gmp_init(
"0x2000", 16);
191 $last = array_pop($ms);
192 $k_i = str_split(substr($k, 0, 1024), 4);
193 foreach ($ms as $mp) {
194 $v = unpack(
'V*', $mp);
195 array_unshift($v,
'N*');
196 $m_i = call_user_func_array(
'pack', $v);
197 $nh = gmp_strval($this->NH($k_i, $m_i, $len), 16);
198 $y .= pack(
'H*', str_pad($nh, 16,
'0', STR_PAD_LEFT));
203 $len = gmp_init(strlen($last) * 8);
204 $last = str_pad($last, max(32, ((strlen($last) + 31) >> 5) << 5),
"\x00");
205 $v = unpack(
'V*', $last);
206 array_unshift($v,
'N*');
207 $m_t = call_user_func_array(
'pack', $v);
208 $k_i = str_split(substr($k, 0, strlen($m_t)), 4);
209 $nh = gmp_strval($this->NH($k_i, $m_t, $len), 16);
210 $y .= pack(
'H*', str_pad($nh, 16,
'0', STR_PAD_LEFT));
214 protected function NH($k_i, $m, $len)
217 $m_i = str_split($m, 4);
222 for ($i = 0, $t = count($m_i) >> 3; $i < $t; $i++) {
223 for ($j = 0; $j < 4; $j++) {
229 gmp_init(bin2hex($m_i[8 * $i + $j]), 16),
230 gmp_init(bin2hex($k_i[8 * $i + $j]), 16)
236 gmp_init(bin2hex($m_i[8 * $i + $j + 4]), 16),
237 gmp_init(bin2hex($k_i[8 * $i + $j + 4]), 16)
245 $y = gmp_mod(gmp_add($y, $len), self::$twop64);
249 protected function l2Hash($k, $m)
252 $mask64 = gmp_init(
'0x01ffffff01ffffff', 16);
253 $mask128 = gmp_init(
'0x01ffffff01ffffff01ffffff01ffffff', 16);
254 $k64 = gmp_and(gmp_init(bin2hex(substr($k, 0, 8)), 16), $mask64);
255 $k128 = gmp_and(gmp_init(bin2hex(substr($k, 8, 16)), 16), $mask128);
260 if (strlen($m) <= (1 << 17)) {
266 gmp_init(
'0x10000000000000000', 16),
267 gmp_init(
'0x100000000', 16)
273 $m_1 = substr($m, 0, 1 << 17);
274 $m_2 = substr($m, 1 << 17) .
"\x80";
275 $m_2 = str_pad($m_2, max(16, ((strlen($m_2) + 15) >> 4) << 4),
"\x00");
279 gmp_sub(gmp_pow(2, 64), gmp_pow(2, 32)),
285 gmp_sub(gmp_pow(2, 128), gmp_pow(2, 96)),
287 pack(
'H*', substr(str_repeat(
'00', 16) . gmp_strval($y, 16), -32)) . $m_2
291 $res = substr(str_repeat(
'00', 16) . gmp_strval($y, 16), -32);
292 return pack(
'H*', $res);
295 protected function POLY($wordbits, $maxwordrange, $k, $m)
297 $wordbytes = $wordbits >> 3;
298 $p = gmp_init(constant(__CLASS__ .
'::PRIME_' . $wordbits), 16);
299 $offset = gmp_sub(gmp_pow(2, $wordbits), $p);
300 $marker = gmp_sub($p, 1);
303 $m_i = str_split($m, $wordbytes);
308 for ($i = 0, $n = count($m_i); $i < $n; $i++) {
309 $m = gmp_init(bin2hex($m_i[$i]), 16);
310 if (gmp_cmp($m, $maxwordrange) >= 0) {
311 $y = gmp_mod(gmp_add(gmp_mul($k, $y), $marker), $p);
312 $y = gmp_mod(gmp_add(gmp_mul($k, $y), gmp_sub($m, $offset)), $p);
314 $y = gmp_mod(gmp_add(gmp_mul($k, $y), $m), $p);
321 protected function l3Hash($k1, $k2, $m)
324 $prime36 = gmp_init(self::PRIME_36, 16);
325 for ($i = 0; $i < 8; $i++) {
326 $m_i = gmp_init(bin2hex(substr($m, $i << 1, 2)), 16);
327 $k_i = gmp_mod(gmp_init(bin2hex(substr($k1, $i << 3, 8)), 16), $prime36);
328 $y = gmp_add($y, gmp_mul($m_i, $k_i));
330 $y = gmp_and(gmp_mod($y, $prime36),
'0xFFFFFFFF');
331 $y = gmp_xor($y, gmp_init(bin2hex($k2), 16));
332 $y = pack(
'H*', str_pad(gmp_strval($y, 16), 8,
'0', STR_PAD_LEFT));
const PRIME_64
64-bits prime number, in hexadecimal notation.
const PRIME_36
36-bits prime number, in hexadecimal notation.
$blkSize
Block size for the cipher.
$cipher
Cipher algorithm used to encrypt data.
const PRIME_128
128-bits prime number, in hexadecimal notation.
__construct(MacEnum $macAlgorithm, SubAlgorithmAbstractEnum $innerAlgorithm, $key, $nonce= '')
$taglen
Length of tags generated by this instance.