3 * A Cipher based MAC generator (Based upon the CMAC specification)
7 * @see http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
8 * @category PHPCryptLib
10 * @subpackage Implementation
11 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
12 * @copyright 2011 The Authors
13 * @license http://www.opensource.org/licenses/mit-license.html MIT License
14 * @version Build @@version@@
16 namespace CryptLib\MAC\Implementation
;
18 use \CryptLib\Cipher\Factory
;
21 * A Cipher based MAC generator (Based upon the CMAC specification)
23 * @see http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
24 * @category PHPCryptLib
26 * @subpackage Implementation
28 class CMAC
extends \CryptLib\MAC\AbstractMAC
{
30 protected $cipher = null;
33 * @var array The stored options for this instance
35 protected $options = array(
36 'cipher' => 'aes-128',
37 'cipherFactory' => null,
41 * Build the instance of the MAC generator
43 * @param array $options The options for the instance
47 public function __construct(array $options = array()) {
48 parent
::__construct($options);
49 if (is_null($this->options
['cipherFactory'])) {
50 $this->options
['cipherFactory'] = new Factory
;
52 $this->cipher
= $this->options
['cipherFactory']->getBlockCipher(
53 $this->options
['cipher']
58 * Generate the MAC using the supplied data
60 * @param string $data The data to use to generate the MAC with
61 * @param string $key The key to generate the MAC
62 * @param int $size The size of the output to return
64 * @return string The generated MAC of the appropriate size
66 public function generate($data, $key, $size = 0) {
67 $blockSize = $this->cipher
->getBlockSize();
71 if ($size > $blockSize) {
72 throw new \
OutOfRangeException(
74 'The size is too big for the cipher primitive [%d:%d]',
80 $this->cipher
->setKey($key);
81 $keys = $this->generateKeys();
82 $mBlocks = $this->splitDataIntoMBlocks($data, $keys);
83 $cBlock = str_repeat(chr(0), $blockSize);
84 foreach ($mBlocks as $key => $block) {
85 $cBlock = $this->cipher
->encryptBlock($cBlock ^
$block);
87 return substr($cBlock, 0, $size);
91 * Generate a pair of keys by encrypting a block of all 0's, and then
92 * maniuplating the result
94 * @return array The generated keys
96 protected function generateKeys() {
98 $blockSize = $this->cipher
->getBlockSize();
99 $rVal = $this->getRValue($blockSize);
100 $text = str_repeat(chr(0), $blockSize);
101 $lVal = $this->cipher
->encryptBlock($text);
102 $keys[0] = $this->leftShift($lVal, 1);
103 if (ord(substr($lVal, 0, 1)) > 127) {
104 $keys[0] = $keys[0] ^
$rVal;
106 $keys[1] = $this->leftShift($keys[0], 1);
107 if (ord(substr($keys[0], 0, 1)) > 127) {
108 $keys[1] = $keys[1] ^
$rVal;
114 * Get an RValue based upon the block size
116 * @param int $size The size of the block in bytes
118 * @see http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
119 * @return string A RValue of the appropriate block size
121 protected function getRValue($size) {
124 return str_repeat(chr(0), 7) . chr(0x1B);
126 return str_repeat(chr(0), 15) . chr(0x87);
129 throw new \
RuntimeException('Unsupported Block Size For The Cipher');
132 protected function leftShift($data, $bits) {
133 $mask = (0xff << (8 - $bits)) & 0xff;
136 $length = strlen($data);
137 for ($i = $length - 1; $i >= 0; $i--) {
138 $tmp = ord($data[$i]);
139 $result .= chr(($tmp << $bits) |
$state);
140 $state = ($tmp & $mask) >> (8 - $bits);
142 return strrev($result);
146 * Split the data into appropriate block chunks, encoding with the kyes
148 * @param string $data The data to split
149 * @param array $keys The keys to use for encoding
151 * @return array The array of chunked and encoded data
153 protected function splitDataIntoMBlocks($data, array $keys) {
154 $blockSize = $this->cipher
->getBlockSize();
155 $data = str_split($data, $blockSize);
157 if (strlen($last) != $blockSize) {
158 //Pad the last element
159 $last .= chr(0x80) . str_repeat(chr(0), $blockSize - 1 - strlen($last));
160 $last = $last ^
$keys[1];
162 $last = $last ^
$keys[0];
164 $data[count($data) - 1] = $last;