Skip to content

Commit

Permalink
Add an Authenticated Block Mode with full tests, Remove RNGCrypto and…
Browse files Browse the repository at this point in the history
… switch to mcrypt_create_iv for a high strength RNG
  • Loading branch information
ircmaxell committed Jun 27, 2011
1 parent 0833233 commit 37ce385
Show file tree
Hide file tree
Showing 10 changed files with 689 additions and 37 deletions.
8 changes: 6 additions & 2 deletions lib/CryptLib/Cipher/Block/Mode.php
Expand Up @@ -33,14 +33,16 @@ interface Mode {
* @param string $key The key to use for decrypting the data
* @param BlockCipher $cipher The cipher to use for decrypting the data
* @param string $initv The initialization vector to use
* @param string $adata Any additional authenticated data to encrypt/decrypt with
*
* @return string The decrypted data
*/
public function decrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv
$initv,
$adata = ''
);

/**
Expand All @@ -50,14 +52,16 @@ public function decrypt(
* @param string $key The key to use for encrypting the data
* @param BlockCipher $cipher The cipher to use for encrypting the data
* @param string $initv The initialization vector to use
* @param string $adata Any additional authenticated data to encrypt/decrypt with
*
* @return string The encrypted data
*/
public function encrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv
$initv,
$adata = ''
);

/**
Expand Down
8 changes: 6 additions & 2 deletions lib/CryptLib/Cipher/Block/Mode/CBC.php
Expand Up @@ -34,14 +34,16 @@ class CBC implements \CryptLib\Cipher\Block\Mode {
* @param string $key The key to use for decrypting the data
* @param BlockCipher $cipher The cipher to use for decrypting the data
* @param string $iv The initialization vector to use
* @param string $adata Any additional authenticated data to encrypt/decrypt with
*
* @return string The decrypted data
*/
public function decrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv
$initv,
$adata = ''
) {
$size = $cipher->getBlockSize($key);
$blocks = str_split($data, $size);
Expand All @@ -63,14 +65,16 @@ public function decrypt(
* @param string $key The key to use for encrypting the data
* @param BlockCipher $cipher The cipher to use for encrypting the data
* @param string $iv The initialization vector to use
* @param string $adata Any additional authenticated data to encrypt/decrypt with
*
* @return string The encrypted data
*/
public function encrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv
$initv,
$adata = ''
) {
$size = $cipher->getBlockSize($key);
$blocks = str_split($data, $size);
Expand Down
296 changes: 296 additions & 0 deletions lib/CryptLib/Cipher/Block/Mode/CCM.php
@@ -0,0 +1,296 @@
<?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://opensource.org/licenses/bsd-license.php New BSD License
* @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1
* @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 implements \CryptLib\Cipher\Block\Mode {

/**
* @var int The number of octets in the length field
*/
protected $lSize = 4;

/**
* @var int The number of octets in the Authentication field
*/
protected $authFieldSize = 8;

/**
* 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;
}

/**
* 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;
}

/**
* Decrypt the data using the supplied key, cipher and initialization vector
*
* @param string $data The data to decrypt
* @param string $key The key to use for decrypting the data
* @param BlockCipher $cipher The cipher to use for decrypting the data
* @param string $iv The initialization vector to use
* @param string $adata Any additional authenticated data to decrypt with
*
* @return string The decrypted data
*/
public function decrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv,
$adata = ''
) {
$initv = $this->extractInitv($initv, $cipher->getBlockSize($key));
$message = substr($data, 0, -1 * $this->authFieldSize);
$uValue = substr($data, -1 * $this->authFieldSize);
$data = $this->encryptMessage($initv, $key, $message, $uValue, $cipher);
$computedT = substr($data, -1 * $this->authFieldSize);
$data = substr($data, 0, -1 * $this->authFieldSize);
$authFieldT = $this->computeAuthField($initv, $key, $data, $adata, $cipher);
if ($authFieldT != $computedT) {
return false;
}
return rtrim($data, chr(0));
}

/**
* Encrypt the data using the supplied key, cipher and initialization vector
*
* @param string $data The data to encrypt
* @param string $key The key to use for encrypting the data
* @param BlockCipher $cipher The cipher to use for encrypting the data
* @param string $iv The initialization vector to use
* @param string $adata Any additional authenticated data to encrypt with
*
* @return string The encrypted data
*/
public function encrypt(
$data,
$key,
\CryptLib\Cipher\Block\BlockCipher $cipher,
$initv,
$adata = ''
) {
$blockSize = $cipher->getBlockSize($key);
$initv = $this->extractInitv($initv, $blockSize);
$authFieldT = $this->computeAuthField($initv, $key, $data, $adata, $cipher);
$data = $this->encryptMessage($initv, $key, $data, $authFieldT, $cipher);
return $data;
}

/**
* Get the name of the current mode implementation
*
* @return string The current mode name
*/
public function getMode() {
return 'ccm';
}

/**
* Compute the authentication field
*
* @param string $initv The initalization vector
* @param string $key The key to use
* @param string $data The data to compute with
* @param string $adata The authentication data to use
* @param BlockCipher $cipher The cipher implementation
*
* @return string The computed MAC Authentication Code
*/
protected function computeAuthField(
$initv,
$key,
$data,
$adata,
\CryptLib\Cipher\Block\BlockCipher $cipher
) {
$blockSize = $cipher->getBlockSize($key);
$flags = pack(
'C',
64 * (empty($adata) ? 0 : 1)
+ 8 * (($this->authFieldSize - 2) / 2)
+ ($this->lSize - 1)
);
$blocks = array(
$flags . $initv . pack($this->getLPackString(), strlen($data))
);
if (strlen($data) % $blockSize != 0) {
$data .= str_repeat(chr(0), $blockSize - (strlen($data) % $blockSize));
}
if (!empty($adata)) {
if (strlen($adata) < ((1 << 16) - (1 << 8))) {
$len = pack('n', strlen($adata));
} else {
$len = chr(0xff) . chr(0xfe) . pack('N', strlen($adata));
}
$temp = $len . $adata;
if (strlen($temp) % $blockSize != 0) {
//Pad the string to exactly mod16
$temp .= str_repeat(
chr(0),
$blockSize - (strlen($temp) % $blockSize)
);
}
$blocks = array_merge($blocks, str_split($temp, $blockSize));
}
$blocks = array_merge($blocks, str_split($data, $blockSize));
$crypted = array(
1 => $cipher->encryptBlock($blocks[0], $key)
);
$blockLen = count($blocks);
for ($i = 1; $i < $blockLen; $i++) {
$crypted[$i + 1] = $cipher->encryptBlock(
$crypted[$i] ^ $blocks[$i],
$key
);
}
return substr(end($crypted), 0, $this->authFieldSize);
}

/**
* Encrypt the data using the supplied method
*
* @param string $initv The initialization Vector
* @param string $key The key to encrypt with
* @param string $data The data to encrypt
* @param string $authValue The auth value field
* @param BlockCipher $cipher The cipher to use
*
* @return string The encrypted data with authfield payload
*/
protected function encryptMessage(
$initv,
$key,
$data,
$authValue,
\CryptLib\Cipher\Block\BlockCipher $cipher
) {
$blockSize = $cipher->getBlockSize($key);
$flags = pack('C', ($this->lSize - 1));
$blocks = str_split($data, $blockSize);
$sblocks = array();
$blockLen = count($blocks);
for ($i = 0; $i <= $blockLen; $i++) {
$sblocks[] = $cipher->encryptBlock(
$flags . $initv . pack($this->getLPackString(), $i),
$key
);
}
$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;
}

/**
* 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() {
switch ($this->lSize) {
case 2:
return 'n';
case 3:
return 'xn';
case 4:
return 'N';
case 5:
return 'xN';
case 6:
return 'xxN';
case 7:
return 'xxxN';
case 8:
return 'xxxxN';
}
return 'x';
}

}

0 comments on commit 37ce385

Please sign in to comment.