33 throw new \InvalidArgumentException(
'Incompatible cipher (block size != 16)');
36 if ($tagLength > 16) {
37 throw new \InvalidArgumentException(
'Invalid tag length (must be in 0..16)');
40 if (strlen(
$iv) > 15) {
41 throw new \InvalidArgumentException(
'Invalid nonce length (must be in 0..16)');
44 $this->taglen = $tagLength;
47 $this->l = new \fpoirotte\Cryptal\Modes\OCB\Lseries($cipher);
50 protected static function ntz($n)
52 return strcspn(strrev(decbin($n)),
'1');
55 protected function hash($A)
58 $Atail = strlen($A) % 16;
59 $A = str_split($A, 16);
61 $Sum = $Offset = str_repeat(
"\x00", 16);
62 for ($i = 0; $i < $m; $i++) {
63 $Offset ^= $this->l[self::ntz($i + 1)];
64 $Sum ^= $this->cipher->encrypt(
'', $A[$i] ^ $Offset);
68 $Offset ^= $this->l[
'*'];
69 $CipherInput = str_pad($A[$m] .
"\x80", 16,
"\x00") ^ $Offset;
70 $Sum ^= $this->cipher->encrypt(
'', $CipherInput);
78 $options = stream_context_get_options($context);
79 $A = isset($options[
'cryptal'][
'data']) ? (string) $options[
'cryptal'][
'data'] :
'';
81 $m = strlen($data) >> 4;
82 $Ptail = strlen($data) % 16;
83 $P = str_split($data, 16);
85 $Nlen = strlen($this->iv);
86 $Nonce = sprintf(
"%07b", ($this->taglen << 3) % 128) . str_repeat(
'0', 120 - ($Nlen << 3)) .
'1' .
87 vsprintf(str_repeat(
"%08b", $Nlen), array_map(
'ord', str_split($this->iv)));
88 $bottom = bindec(substr($Nonce, 122));
89 $Ktop = array_map(
'bindec', str_split(substr($Nonce, 0, 122) .
'000000', 8));
90 $Ktop = $this->cipher->encrypt(
'', implode(
'', array_map(
'chr', $Ktop)));
91 $Stretch = $Ktop . (substr($Ktop, 0, 8) ^ substr($Ktop, 1, 8));
92 $Offset = vsprintf(str_repeat(
"%08b", 24), array_map(
'ord', str_split($Stretch)));
93 $Offset = substr($Offset, $bottom, 128);
94 $Offset = array_map(
'bindec', str_split($Offset, 8));
95 $Offset = implode(
'', array_map(
'chr', $Offset));
96 $Checksum = str_repeat(
"\x00", 16);
99 for ($i = 0; $i < $m; $i++) {
100 $Offset ^= $this->l[self::ntz($i + 1)];
101 $C .= $Offset ^ $this->cipher->encrypt(
'', $P[$i] ^ $Offset);
106 $Offset ^= $this->l[
'*'];
107 $Pad = $this->cipher->encrypt(
'', $Offset);
109 $Checksum ^= str_pad($P[$m] .
"\x80", 16,
"\x00");
112 $Tag = $this->cipher->encrypt(
'', $Checksum ^ $Offset ^ $this->l[
'$']) ^ $this->hash($A);
113 stream_context_set_option($context,
'cryptal',
'tag', substr($Tag, 0, $this->taglen));
119 $options = stream_context_get_options($context);
120 $A = isset($options[
'cryptal'][
'data']) ? (string) $options[
'cryptal'][
'data'] :
'';
121 $T = isset($options[
'cryptal'][
'tag']) ? (string) $options[
'cryptal'][
'tag'] :
'';
123 $m = strlen($data) >> 4;
124 $Ctail = strlen($data) % 16;
125 $C = str_split($data, 16);
127 $Nlen = strlen($this->iv);
128 $Nonce = sprintf(
"%07b", ($this->taglen << 3) % 128) . str_repeat(
'0', 120 - ($Nlen << 3)) .
'1' .
129 vsprintf(str_repeat(
"%08b", $Nlen), array_map(
'ord', str_split($this->iv)));
130 $bottom = bindec(substr($Nonce, 122));
131 $Ktop = array_map(
'bindec', str_split(substr($Nonce, 0, 122) .
'000000', 8));
132 $Ktop = $this->cipher->encrypt(
'', implode(
'', array_map(
'chr', $Ktop)));
133 $Stretch = $Ktop . (substr($Ktop, 0, 8) ^ substr($Ktop, 1, 8));
134 $Offset = vsprintf(str_repeat(
"%08b", 24), array_map(
'ord', str_split($Stretch)));
135 $Offset = substr($Offset, $bottom, 128);
136 $Offset = array_map(
'bindec', str_split($Offset, 8));
137 $Offset = implode(
'', array_map(
'chr', $Offset));
138 $Checksum = str_repeat(
"\x00", 16);
141 for ($i = 0; $i < $m; $i++) {
142 $Offset ^= $this->l[self::ntz($i + 1)];
143 $decoded = $Offset ^ $this->cipher->decrypt(
'', $C[$i] ^ $Offset);
145 $Checksum ^= $decoded;
149 $Offset ^= $this->l[
'*'];
150 $Pad = $this->cipher->encrypt(
'', $Offset);
151 $Pstar = $C[$m] ^ $Pad;
153 $Checksum ^= str_pad($Pstar .
"\x80", 16,
"\x00");
156 $Tag = $this->cipher->encrypt(
'', $Checksum ^ $Offset ^ $this->l[
'$']) ^ $this->hash($A);
157 if ((
string) substr($Tag, 0, $this->taglen) !== $T) {
158 throw new \InvalidArgumentException(
'Tag does not match expected value');
$taglen
Output tag length (in bytes)
__construct(CryptoInterface $cipher, $iv, $tagLength)
$cipher
Approved block cipher with a 128-bit block size.
$iv
Initialization Vector.