View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
Zend_Crypt
{zone-data}

{zone-data:proposer-list}
[Pádraic Brady|mailto:padraic.brady@yahoo.com]
[Dmitry Stogov|mailto:dmitry@zend.com], Zend liaison
{zone-data}

{zone-data:revision}
1.1 - 12 September 2007
{zone-data}

{zone-data:overview}
The purpose of Zend_Crypt is to offer PHP5 implemented cryptographic and encryption algorithms for use by other components (e.g. Zend_Mail, Zend_OpenId) and application developers themselves. In proposing Zend_Crypt, a primary goal is to reduce reliance on disparate implementations within the framework by offering very flexible implementations which will utilise available PHP5 core extensions. This reduces duplication and centralises maintenance of essential core cryptographic algorithms.

The two initial Zend_Crypt implementations of the Hashed Message Authentication Code (HMAC; RFC 2104) and Diffie-Hellman Key Exchange (DH; RFC 2631) are proposed first since they are required algorithms of the OpenID 2.0 Authentication Specification which is being implemented as Zend_OpenId. Others will follow should the proposal be accepted.

A base Zend_Crypt class will additionally collate static methods for common tasks such as hashing and random number generation which may rely on more than one PHP extensions or functions.
{zone-data}

{zone-data:references}
* [Implementations in subversion|http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/Crypt]

*Related PEAR proposals for PHP5/PEAR2*
* [PEAR Crypt_HMAC2|http://pear.php.net/pepr/pepr-proposal-show.php?id=495]
* [PEAR Crypt_DiffieHellman|http://pear.php.net/pepr/pepr-proposal-show.php?id=496]

*RFC References*
* [RFC 2104: HMAC: Keyed-Hashing for Message Authentication|http://tools.ietf.org/html/rfc2104]
* [RFC 2631: Diffie-Hellman Key Agreement Method|http://tools.ietf.org/html/rfc2631]
{zone-data}

{zone-data:requirements}
* *Must* be accompanied by comprehensive unit tests reflecting any RFCs which illustrate a testing framework
* *Must* implement Hashed Message Authentication Code (RFC2104)
* *Must* implement Diffie-Hellman Key Exchange (RFC2631)
* *Must* implement Math methods for enabling big integer (> 32 bit) support and methods for transforming big integer strings to binary forms, and vice versa.
* *Must* contain ability to use future ext/openssl support for Diffie-Hellman computations.
{zone-data}

{zone-data:dependencies}
* Zend_Exception
{zone-data}

{zone-data:operation}
Zend_Crypt will form a collection of cryptographic and encryption classes. As such each component can be used in isolation, or to perform aggregate operations (e.g. using Diffie-Hellman to negotiate an HMAC). Operation is intended to be flexible, with support for input and output (where warranted) forms like big integers and binary. General purpose static methods will be contained in a base Zend_Crypt class.

Part of each class's responsibility will be to implement each algorithm with support for a range of PHP extensions. This will increase support coverage for all PHP5.1+ versions where arbritrary precision math and hashing extensions may vary widely. At a minimum support for ext/hash, ext/mhash, ext/bcmath, ext/openssl (from PHP 5.3) and ext/gmp will be implemented.

Please refer to Use Cases for additional API overviews.
{zone-data}

{zone-data:milestones}
* Milestone 1: Implement Hashed-Message-Authentication-Code (HMAC) and Diffie-Hellman-Key-Exchange (DH)
* Milestone 2: Verify operation using Unit Tests based on RFC test examples and which test both standard and binary output.
* Milestone 3: Documentation
{zone-data}

{zone-data:class-list}
* Zend_Crypt
* Zend_Crypt_Hmac
* Zend_Crypt_DiffieHellman
* Zend_Crypt_Math
{zone-data}

{zone-data:use-cases}
* All use cases take the form of Unit Tests*

* Zend_Crypt_Hmac *

{code:php}
class Zend_Crypt_HmacTest extends PHPUnit_Framework_TestCase
{

public function testHmacMD5_1()
{
$data = 'Hi There';
$key = str_repeat("\x0b", 16);
$hmac = Zend_Crypt_Hmac::compute($key, 'MD5', $data);
$this->assertEquals('9294727a3638bb1c13f48ef8158bfc9d', $hmac);
}

public function testHmacSHA1_4()
{
$data = str_repeat("\xcd",50);
$key = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19";
$hmac = Zend_Crypt_Hmac::compute($key, 'SHA1', $data);
$this->assertEquals('4c9007f4026250c6bc8414f9bf50c86c2d7235da', $hmac);
}

public function testHmacRIPEMD160_6()
{
$data = 'Test Using Larger Than Block-Size Key - Hash Key First';
$key = str_repeat("\xaa",80);
$hmac = Zend_Crypt_Hmac($key, 'RIPEMD160', $data);
$this->assertEquals('6466ca07ac5eac29e1bd523e5ada7605b791fd8b', $hmac);
}

}
{code}

Please note that these simple Unit Tests are matched with far more realistic tests using big integers. The above are simple test cases used for illustrative purposes. HMAC tests in particular follow an RFC which defines the test data and expected results.

* Zend_Crypt_DiffieHellman *

Diffie-Hellman Key Exchange involved two parties, communicating across an insecure communication channel, negotiating a shared secret key which cannot be guessed or reverse engineered by a third party. If it looks a bit unintuitive - bear in mind the private keys are never exchanged. Without the private keys, a third party can have every single piece of data but remain unable to re-perform the shared key computation.

{code:php}
class Zend_Crypt_DiffieHellmanTest extends PHPUnit_Framework_TestCase
{

public function testDiffieWithSpec()
{
$aliceOptions = array(
'prime'=>'563',
'generator'=>'5',
'private'=>'9'
);
$bobOptions = array(
'prime'=>'563',
'generator'=>'5',
'private'=>'14'
);

$alice = new Zend_Crypt_DiffieHellman($aliceOptions['prime'], $aliceOptions['generator'], $aliceOptions['private']);
$bob = new Zend_Crypt_DiffieHellman($bobOptions['prime'], $bobOptions['generator'], $bobOptions['private']);
$alice->generateKeys();
$bob->generateKeys();

$this->assertEquals('78', $alice->getPublicKey());
$this->assertEquals('534', $bob->getPublicKey());

$aliceSecretKey = $alice->computeSecretKey($bob->getPublicKey());
$bobSecretKey = $bob->computeSecretKey($alice->getPublicKey());

// both Alice and Bob should now have the same secret key
$this->assertEquals('117', $aliceSecretKey);
$this->assertEquals('117', $bobSecretKey);
}

}
{code}

Please note that these simple Unit Tests are matched with far more realistic tests using big integers. The above are simple test cases used for illustrative purposes.
{zone-data}

{zone-data:skeletons}
*Zend_Crypt_Hmac*

{code:php}
/**
* PHP implementation of the RFC 2104 Hash based Message Authentication Code
* algorithm.
*
* @category Zend
* @package Zend_Crypt
* @copyright Copyright (c) 2007 Pádraic Brady (http://blog.astrumfutura.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Crypt_Hmac
{

/**
* The key to use for the hash
*
* @var string
*/
protected static $_key = null;

/**
* pack() format to be used for current hashing method
*
* @var string
*/
protected static $_packFormat = null;

/**
* Hashing algorithm; can be the md5/sha1 functions or any algorithm name
* listed in the output of PHP 5.1.2+ hash_algos().
*
* @var string
*/
protected static $_hashAlgorithm = 'md5';

/**
* Supported direct hashing functions in PHP
*
* @var array
*/
protected static $_supportedHashNativeFunctions = array(
'md5',
'sha1',
);

/**
* List of hash pack formats for each hashing algorithm supported.
* Only required when hash or mhash are not available, and we are
* using either md5() or sha1().
*
* @var array
*/
protected static $_hashPackFormats = array(
'md5' => 'H32',
'sha1' => 'H40'
);

/**
* List of algorithms supported my mhash()
*
* @var array
*/
protected static $_supportedMhashAlgorithms = array('adler32',' crc32', 'crc32b', 'gost',
'haval128', 'haval160', 'haval192', 'haval256', 'md4', 'md5', 'ripemd160',
'sha1', 'sha256', 'tiger', 'tiger128', 'tiger160');

/**
* Constants representing the output mode of the hash algorithm
*/
const STRING = 'string';
const BINARY = 'binary';

/**
* Performs a HMAC computation given relevant details such as Key, Hashing
* algorithm, the data to compute MAC of, and an output format of String,
* Binary notation or BTWOC.
*
* @param string $key
* @param string $hash
* @param string $data
* @param string $output
* @param boolean $internal
* @return string
*/
public static function compute($key, $hash, $data, $output = self::STRING, $internal = false)
{}

/**
* Setter for the hash method. Supports md5() and sha1() functions, and if
* available the hashing algorithms supported by the hash() PHP5 function.
*
* @param string $hash
* @return Zend_Crypt_Hmac
*/
protected static function _setHashAlgorithm($hash)
{}

/**
* Perform HMAC and return the keyed data
*
* @param string $data
* @param string $output
* @param bool $internal Option to not use hash() functions for testing
* @return string
*/
protected static function _hash($data, $output = self::STRING, $internal = false)
{}

/**
* Since MHASH accepts an integer constant representing the hash algorithm
* we need to make a small detour to get the correct integer matching our
* algorithm's name.
*
* @param string $hashAlgorithm
* @return integer
*/
protected static function _getMhashDefinition($hashAlgorithm)
{}

/**
* Digest method when using native functions which allows the selection
* of raw binary output.
*
* @todo Replace with Zend_Crypt::digest() once committed
* @param string $hash
* @param string $key
* @param string $mode
* @return string
*/
protected static function _digest($hash, $key, $mode)
{}

}
{code}

*Zend_Crypt_DiffieHellman*

{code:php}
/**
* PHP implementation of the Diffie-Hellman public key encryption algorithm.
* Allows two unassociated parties to establish a joint shared secret key
* to be used in encrypting subsequent communications.
*
* @category Zend
* @package Zend_Crypt
* @subpackage DiffieHellman
* @copyright Copyright (c) 2007 Pádraic Brady (http://blog.astrumfutura.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Crypt_DiffieHellman
{

/**
* Default large prime number; required by the algorithm.
*
* @var string
*/
private $_prime = null;

/**
* The default generator number. This number must be greater than 0 but
* less than the prime number set.
*
* @var string
*/
private $_generator = null;

/**
* A private number set by the local user. It's optional and will
* be generated if not set.
*
* @var string
*/
private $_privateKey = null;

/**
* BigInteger support object courtesy of Zend_Math
*
* @var Zend_Math_BigInteger
*/
private $_math = null;

/**
* The public key generated by this instance after calling generateKeys().
*
* @var string
*/
private $_publicKey = null;

/**
* The shared secret key resulting from a completed Diffie Hellman
* exchange
*
* @var string
*/
private $_secretKey = null;

/**
* Constants
*/
const BINARY = 'binary';
const NUMBER = 'number';
const BTWOC = 'btwoc';

/**
* Constructor; if set construct the object using the parameter array to
* set values for Prime, Generator and Private.
* If a Private Key is not set, one will be generated at random.
*
* @param string $prime
* @param string $generator
* @param string $privateKey
* @param string $privateKeyType
* @return void
*/
public function __construct($prime, $generator, $privateKey = null, $privateKeyType = self::NUMBER)
{}

/**
* Generate own public key. If a private number has not already been
* set, one will be generated at this stage.
*
* @return Zend_Crypt_DiffieHellman
*/
public function generateKeys()
{}

/**
* Returns own public key for communication to the second party to this
* transaction.
*
* @param string $type
* @return string
*/
public function getPublicKey($type = self::NUMBER)
{}

/**
* Compute the shared secret key based on the public key received from the
* the second party to this transaction. This should agree to the secret
* key the second party computes on our own public key.
* Once in agreement, the key is known to only to both parties.
* By default, the function expects the public key to be in binary form
* which is the typical format when being transmitted.
*
* @param string $publicKey
* @param string $type
* @return string
*/
public function computeSecretKey($publicKey, $type = self::NUMBER)
{}

/**
* Return the computed shared secret key from the DiffieHellman transaction
*
* @param string $type
* @return string
*/
public function getSharedSecretKey($type = self::NUMBER)
{}

/**
* Setter for the value of the prime number
*
* @param string $number
* @return Zend_Crypt_DiffieHellman
*/
public function setPrime($number)
{}

/**
* Getter for the value of the prime number
*
* @return string
*/
public function getPrime()
{}


/**
* Setter for the value of the generator number
*
* @param string $number
* @return Zend_Crypt_DiffieHellman
*/
public function setGenerator($number)
{}

/**
* Getter for the value of the generator number
*
* @return string
*/
public function getGenerator()
{}

/**
* Setter for the value of the private number
*
* @param string $number
* @param string $type
* @return Zend_Crypt_DiffieHellman
*/
public function setPrivateKey($number, $type = self::NUMBER)
{}

/**
* Getter for the value of the private number
*
* @param string $type
* @return string
*/
public function getPrivateKey($type = self::NUMBER)
{}

/**
* Setter to pass an extension parameter which is used to create
* a specific BigInteger instance for a specific extension type.
* Allows manual setting of the class in case of an extension
* problem or bug.
*
* @param string $extension
* @return void
*/
public function setBigIntegerMath($extension = null)
{}

/**
* In the event a private number/key has not been set by the user,
* generate one at random.
*
* @return string
*/
protected function _generatePrivateKey()
{}

}
{code}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>