rfc:improve_hash_hkdf_parameter

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

rfc:improve_hash_hkdf_parameter [2017/03/08 23:43] yohgakirfc:improve_hash_hkdf_parameter [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 2: Line 2:
   * Version: 1.0   * Version: 1.0
   * Create Date: 2017-02-05   * Create Date: 2017-02-05
-  * Discussion Start: 2017-02-13+  * Discussion Start: 2017-03-26
   * Author: Yasuo Ohgaki <yohgaki@ohgaki.net>   * Author: Yasuo Ohgaki <yohgaki@ohgaki.net>
-  * Status: Under Discussion+  * Status: Rejected
   * First Published at: http://wiki.php.net/rfc/improve_hash_hkdf_parameter   * First Published at: http://wiki.php.net/rfc/improve_hash_hkdf_parameter
  
 ===== Introduction ===== ===== Introduction =====
  
-HKDF (HMAC based KDF) is [[https://en.wikipedia.org/wiki/Key_derivation_function|KDF]] (Key Devivation Function) defined by [[https://tools.ietf.org/html/rfc5869|RFC 5869]]. Although the RFC describes HKDF in low level crypto contextHKDF is designed as general purpose key derivation function. It derives secure keys for encryption/validation/authentication/etc from other weak/strong key information such as encryption key, API key, master key, password, etc. However, current hash_hkdf() signature is overly optimized for specialized cryptographic operation. (i.e. To derive "binary" output key with "specified length" for "strong input key". This is hardly be common/most used use case with PHP. In addition, it violates what the RFC recommends.This PHP RFC proposes more generic and suitable signature/behavior to use HKDF with PHP.+Even cryptographic hashes have secret information disclosure/analysis risks with string concatenationsFor examplefollowing code is weak (could be  more vulnerable).
  
-In short, HKDF is a general purpose hash function that is designed to create new key(s) from certain key using cryptographic hash function and HMACHowever, newly introduced hash_hkdf(is optimized for very limited usage.+Example 1: 
 +<code php> 
 +  $new_key = hash('sha256', $secret_key $derivation_key)
 +</code>
  
-TL;DR; +To reduce this risk, HMAC was invented. HMAC is known to be more secure compare to previous example.
  
-Please read "[[#current_status|Current Status]]" section for the issue.+Example 2: 
 +<code php> 
 +  $new_key = hash_hmac('sha256', $secret_key, $derivation_key); 
 +</code> 
 + 
 +Note that HMAC divides keys as distinct parameters for better security.  
 + 
 +When deriving new keys, there are many cases that developers need additional application- and context-specific information such as a protocol number, algorithm identifiers, user identities, etc, to limit derived key context. For the same reason that Example 1 being weak, following code is weak. 
 + 
 +Example 3:  
 +<code php> 
 +  $new_key = hash_hmac('sha256', $secret_key, $derivation_key . $proto_version . $algo . $user_id); 
 +</code> 
 + 
 +To reduce risk, keys and additional information should be divided into separated parameters. 
 + 
 +Example 4: 
 +<code php> 
 +  // $proto_version, $algo, $user_id are non-secret. 
 +  $info = $proto_version . $algo . $user_id; 
 + 
 +  // $prk must be cryptographically strong to derive strong $new_key. 
 +  $prk = hash_hmac('sha256', $secret_key, $derivation_key); 
 + 
 +  // $prk is strong and $info is non-secret, therefore $new_key is secured. 
 +  $new_key = hash_hmac('sha256', $prk, $info); 
 +</code> 
 + 
 +This is the basic HKDF (HMAC based Key Derivation Function) operation. HKDF additionally supports shorting/extending key length for certain crypto tasks. Therefore, natural HKDF function signature would be 
 + 
 +<code php> 
 +  string HKDF(string $hash_algo, string $secret_key, string $derivation_key, string $info [, int $length]); 
 +</code> 
 + 
 +Newly introduced HKDF function (hash_hkdf) has different signature that is inconsistent with hash() and hash_hmac() functions even if hash_hkdf() is simple hash_hmac() extension. 
 + 
 +===== Proposal ===== 
 + 
 +Change hash_hkdf() signature from 
 + 
 +<code php> 
 +string hash_hkdf(string $algo, string $ikm [, int $length = 0 [, string $info = '' [, string $salt = '' ]]]) 
 +Return value: Binary hash value ONLY. 
 +</code> 
 +  * $algo: Hash algorithm. e.g. "sha1", "sha256" 
 +  * $ikm: Secret or non secret Input Key Material. Some kind of key. e.g. Super secret master key, password, API key, etc. 
 +  * $length: Optional Output length. If omitted, default output size of specified hash function.  
 +  * $info: Optional Generated key context. e.g Protocol number, user ID, group ID, applicable URI, etc. 
 +  * $salt: Optional secret or non secret salt value. e.g Random value such as nonce, timestamp, etc. 
 + 
 +to 
 + 
 +<code php> 
 +string hash_hkdf(string $algo, string $ikm, string $salt , string $info [, int $length = 0 [, bool $raw_output = FALSE]]
 +Return value: HEX string hash value by default. 
 +</code> 
 +  * $salt: Required. Set empty string to use without salt. More precedence over $info because HKDF security depends on $salt or $ikm strength. 
 +  * $info: Required. Set empty string to use without info. Optional context but user should use hash_hmac() without $info. 
 +  * $length: Optional. Mostly needless unless users need key derivations for low level crypto operations. 
 +  * $raw_output: Optional. Added for hash API consistency. Specify return value format, raw(binary) or HEX. Majority of PHP use cases would require non binary output and HEX is good enough. 
 + 
 +Note: Only changed/added parts are described. 
 + 
 + 
 +=== Short Rationale - security, parameter importance, hash_hmac() signature === 
 + 
 +Insecure usage is easily possible with current signature. 
 +<code php> 
 +$key = hash_hkdf('sha256', $weak_key); // Generate insecure $key!! <= This isn't secure at all w/o strong salt. 
 +$key = hash_hkdf('sha256', $weak_key, 80); // Generate even more insecure $key!! <= Length does not add strength to OKM. 
 +$key = hash_hkdf('sha256', $weak_key, 80, 'Admin'); // Generate even more insecure $key only usable in 'Admin' context!! <= info does not add strength to OKM because it supposed to be non secret. 
 +</code> 
 + 
 +$salt is described as more important parameter than "info" in RFC 5869, and it recommends there should be appropriate $salt whenever it is possible. $info makes HKDF more valuable because simpler and faster hash_hmac(string $algo , string $data , string $key [, bool $raw_output = false ] ) could be used instead when $info could be omitted. hash_hkdf() could be viewed as hash_hmac() extension. (It is HMAC extension indeed) It should be better to have compatible signatures for API consistency.  
 + 
 +i.e. 
 +hash_hmac(), as well as hash(), already has optional $raw_output parameter 
 +<code php> 
 +string hash_hmac(string $algo, string $data, string $key [, bool $raw_output = FALSE ]) 
 +Return value: HEX string hash value by default. 
 +</code> 
 + 
 +Compatible hash_hkdf() signature would be 
 +<code php> 
 +string hash_hkdf(string $algo, string $ikm, string $salt , string $info [, int $length = 0 [, bool $raw_output = FALSE ]]) 
 +Return value: HEX string hash value by default. 
 +</code> 
 + 
 +For these reasons, $salt and $info are made to required parameters and made return to HEX hash value by default. 
 + 
 + 
 +=== Rationale behind salt as required parameter === 
 + 
 +  * Salt is responsible for IKM and output key security. Other parameters are not. 
 +  * Salt is required as pre-shared key in most cases. Key parameter should have more precedence. 
 +  * Users should use salt always when it is possible as per the RFC. 
 +    * Must use strong salt if it is possible. 
 +    * Must use secret salt if it is possible. 
 +    * Must use strong secret salt when input key is weak. 
 +  * Omitting salt is usually a bad(weak) design. See 'hash_hkdf() applications with PHP' and 'Other Use Cases' section
 + 
 +=== Rationale behind info as required parameter === 
 + 
 +  * hash_hmac() could be used when info has no use. 
 +  * info has well defined purpose, derive key(s) for specific context by non secret information.  
 + 
 +=== Rationale behind changed return value === 
 + 
 +  * HEX string return value and "raw" flag parameter is required to be consistent with existing hash functions.   
 +  * Most use cases with PHP will require string return values. 
 + 
 +hash('sha256', something) and hash_hmac('sha256', something) returns HEX. hash_hkdf() which is HMAC returns "binary" breaks API consistency and disturbs common usage with PHP. 
 + 
 +===== Detailed Rationale ===== 
 + 
 +HKDF (HMAC based KDF) is [[https://en.wikipedia.org/wiki/Key_derivation_function|KDF]] (Key Devivation Function) defined by [[https://tools.ietf.org/html/rfc5869|RFC 5869]]. Although the RFC describes HKDF in low level crypto context, HKDF is designed as general purpose key derivation function. It derives secure keys for encryption/validation/authentication/etc from other weak/strong key information such as encryption key, API key, master key, password, etc. However, current hash_hkdf() signature is overly optimized for specialized cryptographic operation. (i.e. To derive "binary" output key with "specified length" for "strong input key". This is hardly be common/most used use case with PHP. In addition, it violates what the RFC recommends.) This PHP RFC proposes more generic HKDF behavior to use with PHP and consistent signature with other PHP hash functions. 
 + 
 +In short, HKDF is a general purpose hash function that is designed to create new key(s) from certain key using cryptographic hash function and HMAC. However, newly introduced hash_hkdf() is optimized for very limited usage.
  
 == Terms == == Terms ==
Line 166: Line 286:
   * When key(s) must be unique, either IKM or salt must be unique.   * When key(s) must be unique, either IKM or salt must be unique.
  
-Although it may seem IKM and salt is interchangeable, there is important difference that salt must be not be user controllable. salt and info may seem they are interchangeable. However unlike salt, info must always be non secret.  +Although it may seem IKM and salt is interchangeable, there is important difference that salt must be not be user controllable. salt and info may seem they are interchangeable. However unlike salt, info is supposed to be non secret.  
  
 ==== hash_hkdf() behavior and other hash functions ==== ==== hash_hkdf() behavior and other hash functions ====
Line 249: Line 369:
 You may display URL(6) and send password(7) via email.  You may display URL(6) and send password(7) via email. 
  
-Step2: Validating keys+Step2: Validating keys (Check expiration time before this procedure)
   - Ask and get user to enter password ($ikm) that is supposed to be generated by Step1.   - Ask and get user to enter password ($ikm) that is supposed to be generated by Step1.
   - Get salt and hk parameters in the URL. $salt, $hk   - Get salt and hk parameters in the URL. $salt, $hk
Line 310: Line 430:
 You can create expiration enabled URL for limited use with similar steps. This could be used to allow anonymous storage object access with relatively secure manner. Example is AWS S3 presigned URL. You can create expiration enabled URL for limited use with similar steps. This could be used to allow anonymous storage object access with relatively secure manner. Example is AWS S3 presigned URL.
  
- 
-===== Proposal ===== 
- 
-Change hash_hkdf() signature from 
- 
-<code php> 
-string hash_hkdf(string $algo, string $ikm [, int $length = 0 [, string $info = '' [, string $salt = '' ]]]) 
-Return value: Binary hash value ONLY. 
-</code> 
-  * $algo: Hash algorithm. e.g. "sha1", "sha256" 
-  * $ikm: Secret or non secret Input Key Material. Some kind of key. e.g. Super secret master key, password, API key, etc. 
-  * $length: Optional Output length. If omitted, default output size of specified hash function.  
-  * $info: Optional Generated key context. e.g Protocol number, user ID, group ID, applicable URI, etc. 
-  * $salt: Optional secret or non secret salt value. e.g Random value such as nonce, timestamp, etc. 
- 
-to 
- 
-<code php> 
-string hash_hkdf(string $algo, string $ikm, string $salt , string $info [, int $length = 0 [, bool $raw_output = FALSE]]) 
-Return value: HEX string hash value by default. 
-</code> 
-  * $salt: Required. Set empty string to use without salt. More precedence over $info because HKDF security depends on $salt or $ikm strength. 
-  * $info: Required. Set empty string to use without salt. Optional context but user should use hash_hmac() without $info. 
-  * $length: Optional. Mostly needless unless users need key derivations for low level crypto operations. 
-  * $raw_output: Optional. Added for hash API consistency. Specify return value format, raw(binary) or HEX. Majority of PHP use cases would require non binary output and HEX is good enough. 
- 
-Note: Only changed/added parts are described. 
- 
-Insecure usage is easily possible with current signature. 
-<code php> 
-$key = hash_hkdf('sha256', $weak_key); // Generate secure $key!! <= This isn't secure at all w/o strong salt. 
-$key = hash_hkdf('sha256', $weak_key, 80); // Generate even more secure $key!! <= Length does not add strength to OKM. 
-$key = hash_hkdf('sha256', $weak_key, 80, 'Admin'); // Generate even more secure $key only usable in 'Admin' context!! <= info does not add strength to OKM because it supposed to be non secret. 
-</code> 
- 
-$salt is described as more important parameter than "info" in RFC 5869, and it recommends there should be appropriate $salt whenever it is possible. $info makes HKDF more valuable because simpler and faster hash_hmac(string $algo , string $data , string $key [, bool $raw_output = false ] ) could be used instead when $info could be omitted. hash_hkdf() could be viewed as hash_hmac() extension. (It is HMAC extension indeed) It should be better to have compatible signatures for API consistency.  
- 
-i.e. 
-hash_hmac(), as well as hash(), already has optional $raw_output parameter 
-<code php> 
-string hash_hmac(string $algo, string $data, string $key [, bool $raw_output = FALSE ]) 
-Return value: HEX string hash value by default. 
-</code> 
- 
-Compatible hash_hkdf() signature would be 
-<code php> 
-string hash_hkdf(string $algo, string $ikm, string $salt , string $info [, int $length = 0 [, bool $raw_output = FALSE ]]) 
-Return value: HEX string hash value by default. 
-</code> 
- 
-For these reasons, $salt and $info are made to required parameters and made return to HEX hash value by default. 
- 
- 
-=== Rationale behind salt as required parameter === 
- 
-  * Salt is responsible for IKM and output key security. Other parameters are not. 
-  * Salt is required as pre-shared key in most cases. Key parameter should have more precedence. 
-  * Users should use salt always when it is possible as per the RFC. 
-    * Must use strong salt if it is possible. 
-    * Must use secret salt if it is possible. 
-    * Must use strong secret salt when input key is weak. 
-  * Omitting salt is usually a bad(weak) design. See 'hash_hkdf() applications with PHP' and 'Other Use Cases' section. 
- 
-=== Rationale behind info as required parameter === 
- 
-  * hash_hmac() could be used when info has no use. 
-  * info has well defined purpose, derive key(s) for specific context by non secret information.  
- 
-=== Rationale behind changed return value === 
- 
-  * HEX string return value and "raw" flag parameter is required to be consistent with existing hash functions.   
-  * Most use cases with PHP will require string return values. 
- 
-hash('sha256', something) and hash_hmac('sha256', something) returns HEX. hash_hkdf() which is HMAC returns "binary" breaks API consistency and disturbs common usage with PHP. 
  
 ===== Discussions ===== ===== Discussions =====
Line 619: Line 665:
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-None. hash_hkdf() is new function.+It is merged into PHP 7.1.2.
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-Next PHP 7.x+Next PHP 7.x and 7.1.x
  
 ===== RFC Impact ===== ===== RFC Impact =====
  
-None.+PHP 7.1.2/7.1.3 has hash_hkdf().
  
 ===== Open Issues ===== ===== Open Issues =====
Line 635: Line 681:
 ===== Unaffected PHP Functionality ===== ===== Unaffected PHP Functionality =====
  
-Nothing is affected. hash_hkdf() is new function does not affect any.+Other than hash_hkdf() signature and return value, nothing is affected.
  
 ===== Future Scope ===== ===== Future Scope =====
Line 644: Line 690:
 State whether this project requires a 2/3 State whether this project requires a 2/3
  
 +<doodle title="Fix hash_hkdf() signature and behavior" auth="yohgaki" voteType="single" closed="true">
 +   * Yes
 +   * No
 +</doodle>
  
 +Vote start: 2017-03-26
 +Vote end: 2017-04-07 UTC 23:59:59
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
rfc/improve_hash_hkdf_parameter.1489016608.txt.gz · Last modified: 2017/09/22 13:28 (external edit)