- /**
- * Singleton instance for public use
- */
- protected static $singleton = null;
-
- /**
- * The persistant cache
- */
- protected $cache = null;
-
- /**
- * Cache key we'll use for our salt
- */
- protected $cacheKey = null;
-
- /**
- * The hash algorithm being used
- */
- protected $algorithm = null;
-
- /**
- * binary string, the salt for the HKDF
- */
- protected $salt;
-
- /**
- * The pseudorandom key
- */
- private $prk;
-
- /**
- * The secret key material. This must be kept secret to preserve
- * the security properties of this RNG.
- */
- private $skm;
-
- /**
- * The last block (K(i)) of the most recent expanded key
- */
- protected $lastK;
-
- /**
- * a "context information" string CTXinfo (which may be null)
- * See http://eprint.iacr.org/2010/264.pdf Section 4.1
- */
- protected $context = [];
-
- /**
- * Round count is computed based on the hash'es output length,
- * which neither php nor openssl seem to provide easily.
- */
- public static $hashLength = [
- 'md5' => 16,
- 'sha1' => 20,
- 'sha224' => 28,
- 'sha256' => 32,
- 'sha384' => 48,
- 'sha512' => 64,
- 'ripemd128' => 16,
- 'ripemd160' => 20,
- 'ripemd256' => 32,
- 'ripemd320' => 40,
- 'whirlpool' => 64,
- ];
-
- /**
- * @param string $secretKeyMaterial
- * @param string $algorithm Name of hashing algorithm
- * @param BagOStuff $cache
- * @param string|array $context Context to mix into HKDF context
- * @throws MWException
- */
- public function __construct( $secretKeyMaterial, $algorithm, $cache, $context ) {
- if ( strlen( $secretKeyMaterial ) < 16 ) {
- throw new MWException( "MWCryptHKDF secret was too short." );
- }
- $this->skm = $secretKeyMaterial;
- $this->algorithm = $algorithm;
- $this->cache = $cache;
- $this->salt = ''; // Initialize a blank salt, see getSaltUsingCache()
- $this->prk = '';
- $this->context = is_array( $context ) ? $context : [ $context ];
-
- // To prevent every call from hitting the same memcache server, pick
- // from a set of keys to use. mt_rand is only use to pick a random
- // server, and does not affect the security of the process.
- $this->cacheKey = wfMemcKey( 'HKDF', mt_rand( 0, 16 ) );
- }
-
- /**
- * Save the last block generated, so the next user will compute a different PRK
- * from the same SKM. This should keep things unpredictable even if an attacker
- * is able to influence CTXinfo.
- */
- function __destruct() {
- if ( $this->lastK ) {
- $this->cache->set( $this->cacheKey, $this->lastK );
- }
- }
-
- /**
- * MW specific salt, cached from last run
- * @return string Binary string
- */
- protected function getSaltUsingCache() {
- if ( $this->salt == '' ) {
- $lastSalt = $this->cache->get( $this->cacheKey );
- if ( $lastSalt === false ) {
- // If we don't have a previous value to use as our salt, we use
- // 16 bytes from MWCryptRand, which will use a small amount of
- // entropy from our pool. Note, "XTR may be deterministic or keyed
- // via an optional “salt value” (i.e., a non-secret random
- // value)..." - http://eprint.iacr.org/2010/264.pdf. However, we
- // use a strongly random value since we can.
- $lastSalt = MWCryptRand::generate( 16 );
- }
- // Get a binary string that is hashLen long
- $this->salt = hash( $this->algorithm, $lastSalt, true );
- }
- return $this->salt;
- }
-