MediaWiki master
CryptHKDF.php
Go to the documentation of this file.
1<?php
2
4
36class CryptHKDF {
37
41 protected $cache = null;
42
46 protected $cacheKey = null;
47
51 protected $algorithm = null;
52
57 protected $salt = '';
58
62 private $prk = '';
63
70 private $skm;
71
75 protected $lastK;
76
83 protected $context = [];
84
91 public static $hashLength = [
92 'md5' => 16,
93 'sha1' => 20,
94 'sha224' => 28,
95 'sha256' => 32,
96 'sha384' => 48,
97 'sha512' => 64,
98 'ripemd128' => 16,
99 'ripemd160' => 20,
100 'ripemd256' => 32,
101 'ripemd320' => 40,
102 'whirlpool' => 64,
103 ];
104
112 public function __construct( $secretKeyMaterial, $algorithm, BagOStuff $cache, $context ) {
113 if ( strlen( $secretKeyMaterial ) < 16 ) {
114 throw new InvalidArgumentException( "secret was too short." );
115 }
116 $this->skm = $secretKeyMaterial;
117 $this->algorithm = $algorithm;
118 $this->cache = $cache;
119 $this->context = is_array( $context ) ? $context : [ $context ];
120
121 // To prevent every call from hitting the same memcache server, pick
122 // from a set of keys to use. mt_rand is only use to pick a random
123 // server, and does not affect the security of the process.
124 $this->cacheKey = $cache->makeKey( 'HKDF', mt_rand( 0, 16 ) );
125 }
126
132 public function __destruct() {
133 if ( $this->lastK ) {
134 $this->cache->set( $this->cacheKey, $this->lastK );
135 }
136 }
137
142 protected function getSaltUsingCache() {
143 if ( $this->salt == '' ) {
144 $lastSalt = $this->cache->get( $this->cacheKey );
145 if ( $lastSalt === false ) {
146 // If we don't have a previous value to use as our salt, we use
147 // 16 bytes from random_bytes(), which will use a small amount of
148 // entropy from our pool. Note, "XTR may be deterministic or keyed
149 // via an optional “salt value” (i.e., a non-secret random
150 // value)..." - http://eprint.iacr.org/2010/264.pdf. However, we
151 // use a strongly random value since we can.
152 $lastSalt = random_bytes( 16 );
153 }
154 // Get a binary string that is hashLen long
155 $this->salt = hash( $this->algorithm, $lastSalt, true );
156 }
157 return $this->salt;
158 }
159
168 public function generate( $bytes, $context = '' ) {
169 if ( $this->prk === '' ) {
170 $salt = $this->getSaltUsingCache();
171 $this->prk = self::HKDFExtract(
172 $this->algorithm,
173 $salt,
174 $this->skm
175 );
176 }
177
178 $CTXinfo = implode( ':', array_merge( $this->context, [ $context ] ) );
179
180 return self::HKDFExpand(
181 $this->algorithm,
182 $this->prk,
183 $CTXinfo,
184 $bytes,
185 $this->lastK
186 );
187 }
188
218 public static function HKDF( $hash, $ikm, $salt, $info, $L ) {
219 $prk = self::HKDFExtract( $hash, $salt, $ikm );
220 $okm = self::HKDFExpand( $hash, $prk, $info, $L );
221 return $okm;
222 }
223
234 private static function HKDFExtract( $hash, $salt, $ikm ) {
235 return hash_hmac( $hash, $ikm, $salt, true );
236 }
237
253 private static function HKDFExpand( $hash, $prk, $info, $bytes, &$lastK = '' ) {
254 $hashLen = self::$hashLength[$hash];
255 $rounds = ceil( $bytes / $hashLen );
256 $output = '';
257
258 if ( $bytes > 255 * $hashLen ) {
259 throw new InvalidArgumentException( 'Too many bytes requested from HDKFExpand' );
260 }
261
262 // K(1) = HMAC(PRK, CTXinfo || 1);
263 // K(i) = HMAC(PRK, K(i-1) || CTXinfo || i); 1 < i <= t;
264 for ( $counter = 1; $counter <= $rounds; ++$counter ) {
265 $lastK = hash_hmac(
266 $hash,
267 $lastK . $info . chr( $counter ),
268 $prk,
269 true
270 );
271 $output .= $lastK;
272 }
273
274 return substr( $output, 0, $bytes );
275 }
276}
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:75
__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:91
string $algorithm
The hash algorithm being used.
Definition CryptHKDF.php:51
string $salt
binary string, the salt for the HKDF
Definition CryptHKDF.php:57
BagOStuff $cache
The persistent cache.
Definition CryptHKDF.php:41
__construct( $secretKeyMaterial, $algorithm, BagOStuff $cache, $context)
string $cacheKey
Cache key we'll use for our salt.
Definition CryptHKDF.php:46
array $context
a "context information" string CTXinfo (which may be null) See http://eprint.iacr....
Definition CryptHKDF.php:83
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:88
makeKey( $keygroup,... $components)
Make a cache key from the given components, in the default keyspace.