add methods to decrypt return data from router
[GitHub/Stricted/speedport-hybrid-php-api.git] / CryptLib / Cipher / Block / Mode / CCM.php
1 <?php
2 /**
3 * The CCM (Counter CBC-MAC) mode implementation
4 *
5 * PHP version 5.3
6 *
7 * @category PHPCryptLib
8 * @package Cipher
9 * @subpackage Block
10 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
11 * @copyright 2011 The Authors
12 * @license http://www.opensource.org/licenses/mit-license.html MIT License
13 * @version Build @@version@@
14 * @see http://tools.ietf.org/html/rfc3610
15 */
16
17 namespace CryptLib\Cipher\Block\Mode;
18
19 /**
20 * The CCM (Counter CBC-MAC) mode implementation
21 *
22 * @category PHPCryptLib
23 * @package Cipher
24 * @subpackage Block
25 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
26 * @see http://tools.ietf.org/html/rfc3610
27 */
28 class CCM extends \CryptLib\Cipher\Block\AbstractMode {
29
30 /**
31 * Indicates which mode the cipher mode is in (encryption/decryption)
32 */
33 const MODE_DECRYPT = 1;
34
35 /**
36 * Indicates which mode the cipher mode is in (encryption/decryption)
37 */
38 const MODE_ENCRYPT = 2;
39
40 /**
41 * @var int The number of octets in the Authentication field
42 */
43 protected $authFieldSize = 8;
44
45 /**
46 * @var string The data buffer for this encryption instance
47 */
48 protected $data = '';
49
50 /**
51 * @var int The number of octets in the length field
52 */
53 protected $lSize = 4;
54
55 /**
56 * @var string The mode name for the current instance
57 */
58 protected $mode = 'ccm';
59
60 /**
61 * @var int The current encryption mode (enc/dec)
62 */
63 protected $encryptionMode = 0;
64
65 /**
66 * @var array Mode specific options
67 */
68 protected $options = array(
69 'adata' => '',
70 'lSize' => 4,
71 'aSize' => 8,
72 );
73
74 /**
75 * @var string The initialization vector to use for this instance
76 */
77 protected $usedIV = '';
78
79 /**
80 * Build the instance of the cipher mode
81 *
82 * @param Cipher $cipher The cipher to use for encryption/decryption
83 * @param string $initv The initialization vector (empty if not needed)
84 * @param array $options An array of mode-specific options
85 */
86 public function __construct(
87 \CryptLib\Cipher\Block\Cipher $cipher,
88 $initv,
89 array $options = array()
90 ) {
91 $this->options = $options + $this->options;
92 $this->cipher = $cipher;
93 $this->initv = $initv;
94 $this->adata = $this->options['adata'];
95 $this->setLSize($this->options['lSize']);
96 $this->setAuthFieldSize($this->options['aSize']);
97 $this->reset();
98 }
99
100 /**
101 * Finish the mode and append any additional data necessary
102 *
103 * @return string Any additional data
104 */
105 public function finish() {
106 $mask = (static::MODE_DECRYPT | static::MODE_ENCRYPT);
107 if (!($this->encryptionMode ^ $mask)) {
108 throw new \LogicException('Cannot encrypt and decrypt in same state');
109 }
110 if ($this->encryptionMode & static::MODE_DECRYPT) {
111 return $this->decryptBlockFinal();
112 } else {
113 return $this->encryptBlockFinal();
114 }
115 }
116
117 /**
118 * Set the auth field size to a different value.
119 *
120 * Valid values: 4, 6, 8, 10, 12, 14, 16
121 *
122 * Note that increasing this size will make it harder for an attacker to
123 * modify the message payload
124 *
125 * @param int $new The new size of auth field to append
126 *
127 * @return void
128 * @throws InvalidArgumentException If the number is outside of the range
129 */
130 public function setAuthFieldSize($new) {
131 if (!in_array($new, array(4, 6, 8, 10, 12, 14, 16))) {
132 throw new \InvalidArgumentException(
133 'The Auth Field must be one of: 4, 6, 8, 10, 12, 14, 16'
134 );
135 }
136 $this->authFieldSize = (int) $new;
137 $this->reset();
138 }
139
140 /**
141 * Set the size of the length field. This is a tradeoff between the maximum
142 * message size and the size of the initialization vector
143 *
144 * Valid values are 2, 3, 4, 5, 6, 7, 8
145 *
146 * @param int $new The new LSize to use
147 *
148 * @return void
149 * @throws InvalidArgumentException If the number is outside of the range
150 */
151 public function setLSize($new) {
152 if ($new < 2 || $new > 8) {
153 throw new \InvalidArgumentException(
154 'The LSize must be between 2 and 8 inclusive'
155 );
156 }
157 $this->lSize = (int) $new;
158 $this->reset();
159 }
160
161 /**
162 * Reset the mode to start over (destroying any intermediate state)
163 *
164 * @return void
165 */
166 public function reset() {
167 $this->usedIV = $this->extractInitv(
168 $this->initv,
169 $this->cipher->getBlockSize()
170 );
171 $this->encryptionMode = 0;
172 $this->data = '';
173 }
174
175 /**
176 * Decrypt the data using the supplied key, cipher
177 *
178 * @param string $data The data to decrypt
179 *
180 * @return string The decrypted data
181 */
182 protected function decryptBlock($data) {
183 $this->data .= $data;
184 $this->encryptionMode |= static::MODE_DECRYPT;
185 }
186
187 /**
188 * Perform the decryption of the block data
189 *
190 * @return string The final data
191 */
192 protected function decryptBlockFinal() {
193 $message = substr($this->data, 0, -1 * $this->authFieldSize);
194 $uValue = substr($this->data, -1 * $this->authFieldSize);
195 $data = $this->encryptMessage($message, $uValue);
196 $computedT = substr($data, -1 * $this->authFieldSize);
197 $data = substr($data, 0, -1 * $this->authFieldSize);
198 $authFieldT = $this->computeAuthField($data);
199 if ($authFieldT != $computedT) {
200 return false;
201 }
202 return rtrim($data, chr(0));
203 }
204
205 /**
206 * Encrypt the data using the supplied key, cipher
207 *
208 * @param string $data The data to encrypt
209 *
210 * @return string The encrypted data
211 */
212 protected function encryptBlock($data) {
213 $this->data .= $data;
214 $this->encryptionMode |= static::MODE_ENCRYPT;
215 }
216
217 /**
218 * Perform the encryption of the block data
219 *
220 * @return string The final data
221 */
222 protected function encryptBlockFinal() {
223 $authFieldT = $this->computeAuthField($this->data);
224 $data = $this->encryptMessage($this->data, $authFieldT);
225 return $data;
226 }
227
228 /**
229 * Compute the authentication field
230 *
231 * @param string $data The data to compute with
232 *
233 * @return string The computed MAC Authentication Code
234 */
235 protected function computeAuthField($data) {
236 $blockSize = $this->cipher->getBlockSize();
237 $flags = pack(
238 'C',
239 64 * (empty($this->adata) ? 0 : 1)
240 + 8 * (($this->authFieldSize - 2) / 2)
241 + ($this->lSize - 1)
242 );
243 $blocks = array(
244 $flags . $this->usedIV . pack($this->getLPackString(), strlen($data))
245 );
246 if (strlen($data) % $blockSize != 0) {
247 $data .= str_repeat(chr(0), $blockSize - (strlen($data) % $blockSize));
248 }
249
250 $blocks = array_merge(
251 $blocks,
252 $this->processAData($this->adata, $blockSize)
253 );
254 if (!empty($data)) {
255 $blocks = array_merge($blocks, str_split($data, $blockSize));
256 }
257 $crypted = array(
258 1 => $this->cipher->encryptBlock($blocks[0])
259 );
260
261 $blockLen = count($blocks);
262 for ($i = 1; $i < $blockLen; $i++) {
263 $crypted[$i + 1] = $this->cipher->encryptBlock(
264 $crypted[$i] ^ $blocks[$i]
265 );
266 }
267 return substr(end($crypted), 0, $this->authFieldSize);
268 }
269
270 /**
271 * Encrypt the data using the supplied method
272 *
273 * @param string $data The data to encrypt
274 * @param string $authValue The auth value field
275 *
276 * @return string The encrypted data with authfield payload
277 */
278 protected function encryptMessage($data, $authValue) {
279 $blockSize = $this->cipher->getBlockSize();
280 $flags = pack('C', ($this->lSize - 1));
281 $blocks = str_split($data, $blockSize);
282 $sblocks = array();
283 $blockLen = count($blocks);
284 for ($i = 0; $i <= $blockLen; $i++) {
285 $sblocks[] = $this->cipher->encryptBlock(
286 $flags . $this->usedIV . pack($this->getLPackString(), $i)
287 );
288 }
289 $encrypted = '';
290 foreach ($blocks as $key => $value) {
291 if (strlen($value) < $blockSize) {
292 $sblocks[$key + 1] = substr($sblocks[$key + 1], 0, strlen($value));
293 }
294 $encrypted .= $sblocks[$key + 1] ^ $value;
295 }
296 $sValue = substr($sblocks[0], 0, $this->authFieldSize);
297 $uValue = $authValue ^ $sValue;
298 return $encrypted . $uValue;
299 }
300
301 /**
302 * Enforce the data block is the correct size for the cipher
303 *
304 * @param string $data The data to check
305 *
306 * @return void
307 * @throws InvalidArgumentException if the block size is not correct
308 */
309 protected function enforceBlockSize($data) {
310 return true;
311 }
312
313 /**
314 * Extract the nonce from the initialization vector
315 *
316 * @param string $initv The initialization Vector to trim
317 * @param int $blockSize The size of the final nonce
318 *
319 * @return string The sized nonce
320 * @throws InvalidArgumentException if the IV is too short
321 */
322 protected function extractInitv($initv, $blockSize) {
323 $initSize = $blockSize - 1 - $this->lSize;
324 if (strlen($initv) < $initSize) {
325 throw new \InvalidArgumentException(sprintf(
326 'Supplied Initialization Vector is too short, should be %d bytes',
327 $initSize
328 ));
329 }
330 return substr($initv, 0, $initSize);
331 }
332
333 /**
334 * Get a packing string related to the instance lSize variable
335 *
336 * @return string The pack() string to use to pack the length variables
337 * @see pack()
338 */
339 protected function getLPackString() {
340 if ($this->lSize <= 3) {
341 return str_repeat('x', $this->lSize - 2) . 'n';
342 }
343 return str_repeat('x', $this->lSize - 4) . 'N';
344 }
345
346 /**
347 * Process the Authentication data for authenticating
348 *
349 * @param string $adata The data to authenticate with
350 * @param int $blockSize The block size for the cipher
351 *
352 * @return array An array of strings bound by the supplied blocksize
353 */
354 protected function processAData($adata, $blockSize) {
355 if (!empty($this->adata)) {
356 if (strlen($this->adata) < ((1 << 16) - (1 << 8))) {
357 $len = pack('n', strlen($this->adata));
358 } else {
359 $len = chr(0xff) . chr(0xfe) . pack('N', strlen($this->adata));
360 }
361 $temp = $len . $this->adata;
362 if (strlen($temp) % $blockSize != 0) {
363 //Pad the string to exactly mod16
364 $temp .= str_repeat(
365 chr(0),
366 $blockSize - (strlen($temp) % $blockSize)
367 );
368 }
369 return str_split($temp, $blockSize);
370 }
371 return array();
372 }
373
374 }