add methods to decrypt return data from router
[GitHub/Stricted/speedport-hybrid-php-api.git] / CryptLib / Cipher / Block / Mode / CCM.php
diff --git a/CryptLib/Cipher/Block/Mode/CCM.php b/CryptLib/Cipher/Block/Mode/CCM.php
new file mode 100644 (file)
index 0000000..5bffa7e
--- /dev/null
@@ -0,0 +1,374 @@
+<?php
+/**
+ * The CCM (Counter CBC-MAC) mode implementation
+ *
+ * PHP version 5.3
+ *
+ * @category   PHPCryptLib
+ * @package    Cipher
+ * @subpackage Block
+ * @author     Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright  2011 The Authors
+ * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
+ * @version    Build @@version@@
+ * @see        http://tools.ietf.org/html/rfc3610
+ */
+
+namespace CryptLib\Cipher\Block\Mode;
+
+/**
+ * The CCM (Counter CBC-MAC) mode implementation
+ *
+ * @category   PHPCryptLib
+ * @package    Cipher
+ * @subpackage Block
+ * @author     Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @see        http://tools.ietf.org/html/rfc3610
+ */
+class CCM extends \CryptLib\Cipher\Block\AbstractMode {
+
+    /**
+     * Indicates which mode the cipher mode is in (encryption/decryption)
+     */
+    const MODE_DECRYPT = 1;
+
+    /**
+     * Indicates which mode the cipher mode is in (encryption/decryption)
+     */
+    const MODE_ENCRYPT = 2;
+
+    /**
+     * @var int The number of octets in the Authentication field
+     */
+    protected $authFieldSize = 8;
+
+    /**
+     * @var string The data buffer for this encryption instance
+     */
+    protected $data = '';
+
+    /**
+     * @var int The number of octets in the length field
+     */
+    protected $lSize = 4;
+
+    /**
+     * @var string The mode name for the current instance
+     */
+    protected $mode = 'ccm';
+
+    /**
+     * @var int The current encryption mode (enc/dec)
+     */
+    protected $encryptionMode = 0;
+
+    /**
+     * @var array Mode specific options
+     */
+    protected $options = array(
+        'adata' => '',
+        'lSize' => 4,
+        'aSize' => 8,
+    );
+
+    /**
+     * @var string The initialization vector to use for this instance
+     */
+    protected $usedIV = '';
+
+    /**
+     * Build the instance of the cipher mode
+     *
+     * @param Cipher $cipher  The cipher to use for encryption/decryption
+     * @param string $initv   The initialization vector (empty if not needed)
+     * @param array  $options An array of mode-specific options
+     */
+    public function __construct(
+        \CryptLib\Cipher\Block\Cipher $cipher,
+        $initv,
+        array $options = array()
+    ) {
+        $this->options = $options + $this->options;
+        $this->cipher  = $cipher;
+        $this->initv   = $initv;
+        $this->adata   = $this->options['adata'];
+        $this->setLSize($this->options['lSize']);
+        $this->setAuthFieldSize($this->options['aSize']);
+        $this->reset();
+    }
+
+    /**
+     * Finish the mode and append any additional data necessary
+     *
+     * @return string Any additional data
+     */
+    public function finish() {
+        $mask = (static::MODE_DECRYPT | static::MODE_ENCRYPT);
+        if (!($this->encryptionMode ^ $mask)) {
+            throw new \LogicException('Cannot encrypt and decrypt in same state');
+        }
+        if ($this->encryptionMode & static::MODE_DECRYPT) {
+            return $this->decryptBlockFinal();
+        } else {
+            return $this->encryptBlockFinal();
+        }
+    }
+
+    /**
+     * Set the auth field size to a different value.
+     *
+     * Valid values: 4, 6, 8, 10, 12, 14, 16
+     *
+     * Note that increasing this size will make it harder for an attacker to
+     * modify the message payload
+     *
+     * @param int $new The new size of auth field to append
+     *
+     * @return void
+     * @throws InvalidArgumentException If the number is outside of the range
+     */
+    public function setAuthFieldSize($new) {
+        if (!in_array($new, array(4, 6, 8, 10, 12, 14, 16))) {
+            throw new \InvalidArgumentException(
+                'The Auth Field must be one of: 4, 6, 8, 10, 12, 14, 16'
+            );
+        }
+        $this->authFieldSize = (int) $new;
+        $this->reset();
+    }
+
+    /**
+     * Set the size of the length field.  This is a tradeoff between the maximum
+     * message size and the size of the initialization vector
+     *
+     * Valid values are 2, 3, 4, 5, 6, 7, 8
+     *
+     * @param int $new The new LSize to use
+     *
+     * @return void
+     * @throws InvalidArgumentException If the number is outside of the range
+     */
+    public function setLSize($new) {
+        if ($new < 2 || $new > 8) {
+            throw new \InvalidArgumentException(
+                'The LSize must be between 2 and 8 inclusive'
+            );
+        }
+        $this->lSize = (int) $new;
+        $this->reset();
+    }
+
+    /**
+     * Reset the mode to start over (destroying any intermediate state)
+     *
+     * @return void
+     */
+    public function reset() {
+        $this->usedIV         = $this->extractInitv(
+            $this->initv,
+            $this->cipher->getBlockSize()
+        );
+        $this->encryptionMode = 0;
+        $this->data           = '';
+    }
+
+    /**
+     * Decrypt the data using the supplied key, cipher
+     *
+     * @param string $data The data to decrypt
+     *
+     * @return string The decrypted data
+     */
+    protected function decryptBlock($data) {
+        $this->data .= $data;
+        $this->encryptionMode |= static::MODE_DECRYPT;
+    }
+
+    /**
+     * Perform the decryption of the block data
+     *
+     * @return string The final data
+     */
+    protected function decryptBlockFinal() {
+        $message    = substr($this->data, 0, -1 * $this->authFieldSize);
+        $uValue     = substr($this->data, -1 * $this->authFieldSize);
+        $data       = $this->encryptMessage($message, $uValue);
+        $computedT  = substr($data, -1 * $this->authFieldSize);
+        $data       = substr($data, 0, -1 * $this->authFieldSize);
+        $authFieldT = $this->computeAuthField($data);
+        if ($authFieldT != $computedT) {
+            return false;
+        }
+        return rtrim($data, chr(0));
+    }
+
+    /**
+     * Encrypt the data using the supplied key, cipher
+     *
+     * @param string $data The data to encrypt
+     *
+     * @return string The encrypted data
+     */
+    protected function encryptBlock($data) {
+        $this->data .= $data;
+        $this->encryptionMode |= static::MODE_ENCRYPT;
+    }
+
+    /**
+     * Perform the encryption of the block data
+     *
+     * @return string The final data
+     */
+    protected function encryptBlockFinal() {
+        $authFieldT = $this->computeAuthField($this->data);
+        $data       = $this->encryptMessage($this->data, $authFieldT);
+        return $data;
+    }
+
+    /**
+     * Compute the authentication field
+     *
+     * @param string $data   The data to compute with
+     *
+     * @return string The computed MAC Authentication Code
+     */
+    protected function computeAuthField($data) {
+        $blockSize = $this->cipher->getBlockSize();
+        $flags     = pack(
+            'C',
+            64 * (empty($this->adata) ? 0 : 1)
+                + 8 * (($this->authFieldSize - 2) / 2)
+                + ($this->lSize - 1)
+        );
+        $blocks    = array(
+            $flags . $this->usedIV . pack($this->getLPackString(), strlen($data))
+        );
+        if (strlen($data) % $blockSize != 0) {
+            $data .= str_repeat(chr(0), $blockSize - (strlen($data) % $blockSize));
+        }
+
+        $blocks = array_merge(
+            $blocks,
+            $this->processAData($this->adata, $blockSize)
+        );
+        if (!empty($data)) {
+            $blocks = array_merge($blocks, str_split($data, $blockSize));
+        }
+        $crypted = array(
+            1 => $this->cipher->encryptBlock($blocks[0])
+        );
+
+        $blockLen = count($blocks);
+        for ($i = 1; $i < $blockLen; $i++) {
+            $crypted[$i + 1] = $this->cipher->encryptBlock(
+                $crypted[$i] ^ $blocks[$i]
+            );
+        }
+        return substr(end($crypted), 0, $this->authFieldSize);
+    }
+
+    /**
+     * Encrypt the data using the supplied method
+     *
+     * @param string $data      The data to encrypt
+     * @param string $authValue The auth value field
+     *
+     * @return string The encrypted data with authfield payload
+     */
+    protected function encryptMessage($data, $authValue) {
+        $blockSize = $this->cipher->getBlockSize();
+        $flags     = pack('C', ($this->lSize - 1));
+        $blocks    = str_split($data, $blockSize);
+        $sblocks   = array();
+        $blockLen  = count($blocks);
+        for ($i = 0; $i <= $blockLen; $i++) {
+            $sblocks[] = $this->cipher->encryptBlock(
+                $flags . $this->usedIV . pack($this->getLPackString(), $i)
+            );
+        }
+        $encrypted = '';
+        foreach ($blocks as $key => $value) {
+            if (strlen($value) < $blockSize) {
+                $sblocks[$key + 1] = substr($sblocks[$key + 1], 0, strlen($value));
+            }
+            $encrypted .= $sblocks[$key + 1] ^ $value;
+        }
+        $sValue = substr($sblocks[0], 0, $this->authFieldSize);
+        $uValue = $authValue ^ $sValue;
+        return $encrypted . $uValue;
+    }
+
+    /**
+     * Enforce the data block is the correct size for the cipher
+     *
+     * @param string $data The data to check
+     *
+     * @return void
+     * @throws InvalidArgumentException if the block size is not correct
+     */
+    protected function enforceBlockSize($data) {
+        return true;
+    }
+
+    /**
+     * Extract the nonce from the initialization vector
+     *
+     * @param string $initv     The initialization Vector to trim
+     * @param int    $blockSize The size of the final nonce
+     *
+     * @return string The sized nonce
+     * @throws InvalidArgumentException if the IV is too short
+     */
+    protected function extractInitv($initv, $blockSize) {
+        $initSize = $blockSize - 1 - $this->lSize;
+        if (strlen($initv) < $initSize) {
+            throw new \InvalidArgumentException(sprintf(
+                'Supplied Initialization Vector is too short, should be %d bytes',
+                $initSize
+            ));
+        }
+        return substr($initv, 0, $initSize);
+    }
+
+    /**
+     * Get a packing string related to the instance lSize variable
+     *
+     * @return string The pack() string to use to pack the length variables
+     * @see pack()
+     */
+    protected function getLPackString() {
+        if ($this->lSize <= 3) {
+            return str_repeat('x', $this->lSize - 2) . 'n';
+        }
+        return str_repeat('x', $this->lSize - 4) . 'N';
+    }
+
+    /**
+     * Process the Authentication data for authenticating
+     *
+     * @param string $adata     The data to authenticate with
+     * @param int    $blockSize The block size for the cipher
+     *
+     * @return array An array of strings bound by the supplied blocksize
+     */
+    protected function processAData($adata, $blockSize) {
+        if (!empty($this->adata)) {
+            if (strlen($this->adata) < ((1 << 16) - (1 << 8))) {
+                $len = pack('n', strlen($this->adata));
+            } else {
+                $len = chr(0xff) . chr(0xfe) . pack('N', strlen($this->adata));
+            }
+            $temp = $len . $this->adata;
+            if (strlen($temp) % $blockSize != 0) {
+                //Pad the string to exactly mod16
+                $temp .= str_repeat(
+                    chr(0),
+                    $blockSize - (strlen($temp) % $blockSize)
+                );
+            }
+            return str_split($temp, $blockSize);
+        }
+        return array();
+    }
+
+}