MediaWiki master
CryptHKDF.php
Go to the documentation of this file.
1<?php
33class CryptHKDF {
34
38 protected $cache = null;
39
43 protected $cacheKey = null;
44
48 protected $algorithm = null;
49
54 protected $salt = '';
55
59 private $prk = '';
60
67 private $skm;
68
72 protected $lastK;
73
80 protected $context = [];
81
88 public static $hashLength = [
89 'md5' => 16,
90 'sha1' => 20,
91 'sha224' => 28,
92 'sha256' => 32,
93 'sha384' => 48,
94 'sha512' => 64,
95 'ripemd128' => 16,
96 'ripemd160' => 20,
97 'ripemd256' => 32,
98 'ripemd320' => 40,
99 'whirlpool' => 64,
100 ];
101
109 public function __construct( $secretKeyMaterial, $algorithm, BagOStuff $cache, $context ) {
110 if ( strlen( $secretKeyMaterial ) < 16 ) {
111 throw new InvalidArgumentException( "secret was too short." );
112 }
113 $this->skm = $secretKeyMaterial;
114 $this->algorithm = $algorithm;
115 $this->cache = $cache;
116 $this->context = is_array( $context ) ? $context : [ $context ];
117
118 // To prevent every call from hitting the same memcache server, pick
119 // from a set of keys to use. mt_rand is only use to pick a random
120 // server, and does not affect the security of the process.
121 $this->cacheKey = $cache->makeKey( 'HKDF', mt_rand( 0, 16 ) );
122 }
123
129 public function __destruct() {
130 if ( $this->lastK ) {
131 $this->cache->set( $this->cacheKey, $this->lastK );
132 }
133 }
134
139 protected function getSaltUsingCache() {
140 if ( $this->salt == '' ) {
141 $lastSalt = $this->cache->get( $this->cacheKey );
142 if ( $lastSalt === false ) {
143 // If we don't have a previous value to use as our salt, we use
144 // 16 bytes from random_bytes(), which will use a small amount of
145 // entropy from our pool. Note, "XTR may be deterministic or keyed
146 // via an optional “salt value” (i.e., a non-secret random
147 // value)..." - http://eprint.iacr.org/2010/264.pdf. However, we
148 // use a strongly random value since we can.
149 $lastSalt = random_bytes( 16 );
150 }
151 // Get a binary string that is hashLen long
152 $this->salt = hash( $this->algorithm, $lastSalt, true );
153 }
154 return $this->salt;
155 }
156
165 public function generate( $bytes, $context = '' ) {
166 if ( $this->prk === '' ) {
167 $salt = $this->getSaltUsingCache();
168 $this->prk = self::HKDFExtract(
169 $this->algorithm,
170 $salt,
171 $this->skm
172 );
173 }
174
175 $CTXinfo = implode( ':', array_merge( $this->context, [ $context ] ) );
176
177 return self::HKDFExpand(
178 $this->algorithm,
179 $this->prk,
180 $CTXinfo,
181 $bytes,
182 $this->lastK
183 );
184 }
185
215 public static function HKDF( $hash, $ikm, $salt, $info, $L ) {
216 $prk = self::HKDFExtract( $hash, $salt, $ikm );
217 $okm = self::HKDFExpand( $hash, $prk, $info, $L );
218 return $okm;
219 }
220
231 private static function HKDFExtract( $hash, $salt, $ikm ) {
232 return hash_hmac( $hash, $ikm, $salt, true );
233 }
234
250 private static function HKDFExpand( $hash, $prk, $info, $bytes, &$lastK = '' ) {
251 $hashLen = self::$hashLength[$hash];
252 $rounds = ceil( $bytes / $hashLen );
253 $output = '';
254
255 if ( $bytes > 255 * $hashLen ) {
256 throw new InvalidArgumentException( 'Too many bytes requested from HDKFExpand' );
257 }
258
259 // K(1) = HMAC(PRK, CTXinfo || 1);
260 // K(i) = HMAC(PRK, K(i-1) || CTXinfo || i); 1 < i <= t;
261 for ( $counter = 1; $counter <= $rounds; ++$counter ) {
262 $lastK = hash_hmac(
263 $hash,
264 $lastK . $info . chr( $counter ),
265 $prk,
266 true
267 );
268 $output .= $lastK;
269 }
270
271 return substr( $output, 0, $bytes );
272 }
273}
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:85
makeKey( $keygroup,... $components)
Make a cache key from the given components, in the default keyspace.
generate( $bytes, $context='')
Produce $bytes of secure random data.
getSaltUsingCache()
MW specific salt, cached from last run.
string $lastK
The last block (K(i)) of the most recent expanded key.
Definition CryptHKDF.php:72
__destruct()
Save the last block generated, so the next user will compute a different PRK from the same SKM.
static HKDF( $hash, $ikm, $salt, $info, $L)
RFC5869 defines HKDF in 2 steps, extraction and expansion.
static int[] $hashLength
Round count is computed based on the hash'es output length, which neither php nor openssl seem to pro...
Definition CryptHKDF.php:88
string $algorithm
The hash algorithm being used.
Definition CryptHKDF.php:48
string $salt
binary string, the salt for the HKDF
Definition CryptHKDF.php:54
BagOStuff $cache
The persistent cache.
Definition CryptHKDF.php:38
__construct( $secretKeyMaterial, $algorithm, BagOStuff $cache, $context)
string $cacheKey
Cache key we'll use for our salt.
Definition CryptHKDF.php:43
array $context
a "context information" string CTXinfo (which may be null) See http://eprint.iacr....
Definition CryptHKDF.php:80